<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 6.0.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/wang-cheng/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/wang-cheng/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/wang-cheng/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/wang-cheng/images/logo.svg" color="#222">

<link rel="stylesheet" href="/wang-cheng/css/main.css">


<link rel="stylesheet" href="/wang-cheng/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"cheng-wang123.gitee.io","root":"/wang-cheng/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
<meta property="og:type" content="website">
<meta property="og:title" content="诚の博客">
<meta property="og:url" content="https://cheng-wang123.gitee.io/wang-cheng/page/3/index.html">
<meta property="og:site_name" content="诚の博客">
<meta property="og:description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="王诚">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="https://cheng-wang123.gitee.io/wang-cheng/page/3/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : true,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>诚の博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/wang-cheng/atom.xml" title="诚の博客" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/wang-cheng/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">诚の博客</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">记录学习中的点点滴滴</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/wang-cheng/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/wang-cheng/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/wang-cheng/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/wang-cheng/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content index posts-expand">
            
      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%85%AD/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%85%AD/" class="post-title-link" itemprop="url">面试题(六)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-23 10:04:22" itemprop="dateCreated datePublished" datetime="2022-03-23T10:04:22+08:00">2022-03-23</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-05-14 18:58:05" itemprop="dateModified" datetime="2022-05-14T18:58:05+08:00">2022-05-14</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="mybatis的优缺点"><a href="#mybatis的优缺点" class="headerlink" title="mybatis的优缺点"></a><strong>mybatis的优缺点</strong></h2><p>优点：</p>
<ol>
<li><p>基于SQL语句编程，相当灵活，不会对应用程序或者数据库的现有设计造成影响，SQL写在xml里，解除了sql与程序代码的耦合，便于统一管理，提供xml标签，支持编写动态SQL语句，并可重用。</p>
</li>
<li><p>与JDBC相比，减少了50%以上的代码量，消除了JDBC大量冗余的代码，不需要手动开关连接</p>
</li>
<li><p>很好的与各种数据库兼容(因为mybatis使用JDBC来连接数据库，所以只要JDBC支持的数据库mybatis都支持)</p>
</li>
<li><p>能够与Spring很好的集成</p>
</li>
<li><p>提供映射标签，支持对象与数据库的ORM字段关系映射；提供对象关系映射标签，支持对象关系组件维护</p>
</li>
</ol>
<p>缺点：</p>
<ol>
<li><p>SQL语句的编写工作量大，尤其当字段多、关联表多时，对开发人员编写SQL语句的功底有一定要求</p>
</li>
<li><p>SQL语句依赖数据库，导致数据库移植性差，不能随意更换数据库</p>
</li>
</ol>
<h2 id="MyBatis与Hibernate对比"><a href="#MyBatis与Hibernate对比" class="headerlink" title="MyBatis与Hibernate对比"></a><strong>MyBatis与Hibernate对比</strong></h2><p>MyBatis主要是一个通过操作SQL来操作数据库的框架</p>
<p>Hibernate是一个ORM框架，可以通过操作类来操作数据库</p>
<p>开发速度对比：</p>
<p>Hibernate的真正掌握要比mybatis难一些。MyBatis框架相对简单容易上手</p>
<p>比起两者的开发速度，不仅仅要考虑两者的特性及性能，更要根据项目需求去考虑究竟哪一个更适合项目开发</p>
<p>比如：一个项目中用到的复杂查询基本没有，就简单的增删改查，这样选择Hibernate好一些，因为基本的SQL语句已经被封装好了，根本不需要你去写sql语句，这就节省了大量时间，但是对于一个大型项目，复杂语句较多，这样再去选择Hibernate就不是一个太好的选择，选择mybatis就会快很多，而且语句的管理比较方便</p>
<p>开发工作量对比：</p>
<p>Hibernate和MyBatis都有相应的代码生成工具。可以生成简单基本的DAO层方法。针对高级查询，MyBatis需要手动编写SQL语句，以及ResultMap。而Hibernate有良好的映射机制，开发者无需关心SQL的生成与结果的映射，可以更专注于业务流程。</p>
<p>sql优化方面；</p>
<p>Hibernate的查询将表中的所有字段查询出来，这一点会有性能消耗。Hibernate也可以自己写SQL来指定需要查询的字段，但这样就破坏了Hibernate开发的简洁性。而MyBatis的SQL是手动编写的，可以按需求指定查询的字段。</p>
<p>对象管理的对比：</p>
<p>Hibernate是完整的对象/关系映射解决方案，它提供了对象状态管理的功能，使开发者不再需要理会底层数据库系统的细节。也就是说，相对于常见的JDBC/SQL持久层方案中需要管理SQL语句，Hibernate采用了更自然的面向对象的视角来持久化Java应用中的数据</p>
<p>换句话说，使用Hibernate的开发者应该总是关注对象的状态(state)，不必考虑SQL语句的执行。这部分细节已经由Hibernate掌管妥当，只有开发者在进行系统性能调优的时候才需要进行了解。而MyBatis在这一块没有文档说明，用户需要对对象自己进行详细的管理(因为MyBatis不是ORM框架，没有对象管理这个概念)</p>
<p>缓存机制对比：</p>
<p>相同点：都可以实现自己的缓存或使用其他第三方缓存方案，创建适配器来完成覆盖缓存行为</p>
<p>不同点：Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置，然后再在具体的表-对象映射中配置是哪种缓存。MyBatis的二级缓存配置都是在每一个具体的表-对象映射中进行详细配置，这样针对不同的表可以自定义不同的缓存机制。并且MyBatis可以在命名空间中共享相同的缓存配置和实例，通过Cache-ref实现</p>
<p>两者比较：</p>
<p>因为Hibernate对查询对象有着良好的管理，用户无需关心SQL。所以在使用二级缓存时如果出现脏数据，系统会报错误并提示</p>
<p>而MyBatis在这方面，使用二级缓存时需要特别小心。如果不能完全确定数据更新操作的波及范围，避免Cache的盲目使用。否则，脏数据的出现会给系统的正常运行带来很大的隐患。</p>
<p>Hibernate功能强大，数据库无关性好，O/R映射能力强，如果你对Hibernate相当精通，并且对Hibernate进行了适当的封装，那么你的项目整个持久层代码会相当简单，需要写的代码很少，开发速度很快</p>
<p>Hibernate的缺点就是学习门槛不低，要精通门槛更高，而且怎么设计O/R映射，在性能和对象模型之间如何权衡取得平衡，以及怎样用好Hibernate方面需要你的经验和能力都很强</p>
<h2 id="和-的区别是什么"><a href="#和-的区别是什么" class="headerlink" title="#{}和${}的区别是什么"></a><strong>#{}和${}的区别是什么</strong></h2><p><code>#&#123;&#125;</code>是预编译处理、是占位符，<code>$&#123;&#125;</code>是字符串替换、是拼接符</p>
<p>MyBatis在处理<code>#&#123;&#125;</code>时，会将sql中的<code>#&#123;&#125;</code>替换为？号，调用PreparedStatement来赋值</p>
<p>MyBatis在处理<code>$&#123;&#125;</code>时，就是把<code>$&#123;&#125;</code>替换为变量的值，调用Statement来赋值</p>
<p><code>#&#123;&#125;</code>的变量替换是在DBMS中、变量替换后，<code>#&#123;&#125;</code>对应的变量自动加上单引号</p>
<p><code>$&#123;&#125;</code>的变量替换是在DBMS外、变量替换后，<code>$&#123;&#125;</code>对应的变量不会加上单引号</p>
<p>使用<code>#&#123;&#125;</code>可以有效的防止SQL注入，提高系统安全性</p>
<h2 id="statement-和-preparedstatement区别"><a href="#statement-和-preparedstatement区别" class="headerlink" title="statement 和 preparedstatement区别"></a><strong>statement 和 preparedstatement区别</strong></h2><p>首先是preparedStatement<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">public void add(Config config)&#123;</span><br><span class="line">    String sql = <span class="string">&quot;insert into category values(null,?,?)&quot;</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Connection c = <span class="module-access"><span class="module"><span class="identifier">DBUtil</span>.</span></span>get<span class="constructor">Connection()</span>;</span><br><span class="line">        PreparedStatement ps = c.prepare<span class="constructor">Statement(<span class="params">sql</span>)</span>;</span><br><span class="line">        ps.set<span class="constructor">String(1, <span class="params">config</span>.<span class="params">key</span>)</span>;</span><br><span class="line">        ps.set<span class="constructor">String(2, <span class="params">config</span>.<span class="params">value</span>)</span>;</span><br><span class="line">        ps.execute<span class="literal">()</span>;</span><br><span class="line">        ResultSet rs = ps.get<span class="constructor">GeneratedKeys()</span>;</span><br><span class="line">        <span class="keyword">while</span> (rs.next<span class="literal">()</span>) &#123;</span><br><span class="line">            <span class="built_in">int</span> id = rs.get<span class="constructor">Int(1)</span>;</span><br><span class="line">            config.id = id;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; catch (SQLException e) &#123;</span><br><span class="line">        e.print<span class="constructor">StackTrace()</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>statement<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">delete</span><span class="params">( <span class="type">int</span> id)</span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">Connection</span> <span class="variable">c</span> <span class="operator">=</span> DBUtil.getConnection();</span><br><span class="line">        <span class="type">Statement</span> <span class="variable">s</span> <span class="operator">=</span> c.createStatement();</span><br><span class="line">        <span class="type">String</span> <span class="variable">sql</span> <span class="operator">=</span> <span class="string">&quot;delete from category where id = &quot;</span> + id ;</span><br><span class="line">        s.execute(sql);</span><br><span class="line">    &#125;<span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<ol>
<li><p>概念上</p>
<ul>
<li><p>prepareStatement会先初始化SQL，先把这个SQL提交到数据库中进行预处理，多次使用可提高效率。</p>
</li>
<li><p>createStatement不会初始化，没有预处理，每次都是从0开始执行SQL。</p>
</li>
</ul>
</li>
<li><p>变量上</p>
<ul>
<li><p>prepareStatement可以在SQL中用?替换变量；</p>
</li>
<li><p>createStatement不支持 ? 替换变量，只能在sql中拼接参数；</p>
</li>
</ul>
</li>
<li><p>功能上：如果想要删除三条数据</p>
<ul>
<li>对于createStatement，需要写三条语句  <figure class="highlight n1ql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">String sql = &quot;<span class="keyword">delete</span> <span class="keyword">from</span> category <span class="keyword">where</span> id = <span class="number">2</span><span class="string">&quot;  ; </span></span><br><span class="line"><span class="string">String sql = &quot;</span><span class="keyword">delete</span> <span class="keyword">from</span> category <span class="keyword">where</span> id = <span class="number">3</span><span class="string">&quot;  ; </span></span><br><span class="line"><span class="string">String sql = &quot;</span><span class="keyword">delete</span> <span class="keyword">from</span> category <span class="keyword">where</span> id = <span class="number">7</span><span class="string">&quot;  ;</span></span><br></pre></td></tr></table></figure></li>
<li><p>而prepareStatement，通过set不同数据只需要生成一次执行计划，可以重用</p>
  <figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String sql <span class="operator">=</span> <span class="string">&quot;delete from category where id = ？&quot;</span>  <span class="comment">;</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>PreparedStatement是预编译的,对于批量处理可以大大提高效率.也叫JDBC存储过程。</p>
</li>
<li><p>使用createStatement 对象。在对数据库只执行一次性存取的时侯，用 createStatement对象进行处理。PreparedStatement对象的开销比createStatement大，对于一次性操作并不会带来额外的好处。</p>
</li>
<li><p>createStatement每次执行sql语句，相关数据库都要执行sql语句的编译，preparedstatement是预编译得,preparedstatement支持批处理</p>
</li>
</ul>
</li>
</ol>
<h2 id="乐观锁以及乐观锁的实现"><a href="#乐观锁以及乐观锁的实现" class="headerlink" title="乐观锁以及乐观锁的实现"></a><strong>乐观锁以及乐观锁的实现</strong></h2><p>乐观锁假设认为数据一般情况下不会造成冲突，所以在数据进行提交更新的时候，才会正式对数据的冲突与否进行检测，如果发现冲突了，则让返回用户错误的信息，让用户决定如何去做</p>
<p>乐观锁的实现：</p>
<p>使用数据版本（Version）记录机制实现，这是乐观锁最常用的一种实现方式。何谓数据版本？即为数据增加一个版本标识，一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时，将version字段的值一同读出，数据每更新一次，对此version值加一。当我们提交更新的时候，判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对，如果数据库表当前版本号与第一次取出来的version值相等，则予以更新，否则认为是过期数据</p>
<p>1、数据库表设计</p>
<p>task</p>
<p>有三个字段，分别是id,value、version</p>
<p>2.实现</p>
<p>1）先读task表的数据（实际上这个表只有一条记录），得到version的值为versionValue</p>
<p>2)每次更新task表中的value字段时，为了防止发生冲突，需要这样操作</p>
<p>update task set value = newValue,version =  versionValue + 1   where version = versionValue;</p>
<p>只有这条语句执行了，才表明本次更新value字段的值成功</p>
<p>如假设有两个节点A和B都要更新task表中的value字段值，差不多在同一时刻，A节点和B节点从task表中读到的version值为2，那么A节点和B节点在更新value字段值的时候，都操作 update task set value = newValue,version =  3   where version = 2;,实际上只有1个节点执行该SQL语句成功，假设A节点执行成功，那么此时task表的version字段的值是3，B节点再操作update task set value = newValue,version =  3   where version = 2;这条SQL语句是不执行的，这样就保证了更新task表时不发生冲突</p>
<p>项目中使用案例：<br><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 基于乐观锁的更新操作</span></span><br><span class="line"><span class="comment">    * @param editFinance 编辑的账户对象</span></span><br><span class="line"><span class="comment">    * @param queryLockNo 上次查询的乐观锁版本号</span></span><br><span class="line"><span class="comment">    * @return</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">@Override</span><br><span class="line">public <span class="built_in">int</span> update<span class="constructor">ForLockNo(BzFinanceEntity <span class="params">editFinance</span>, <span class="params">int</span> <span class="params">queryLockNo</span>)</span> &#123;</span><br><span class="line"></span><br><span class="line">    editFinance.set<span class="constructor">LockNo(<span class="params">queryLockNo</span> + 1)</span>; <span class="comment">//修改乐观锁版本</span></span><br><span class="line"></span><br><span class="line">    BzFinanceEntityExample example = <span class="keyword">new</span> <span class="constructor">BzFinanceEntityExample()</span>;</span><br><span class="line">    BzFinanceEntityExample.Criteria criteria = example.create<span class="constructor">Criteria()</span>;</span><br><span class="line">    criteria.<span class="keyword">and</span><span class="constructor">IdFinanceEqualTo(<span class="params">editFinance</span>.<span class="params">getIdFinance</span>()</span>);</span><br><span class="line">    criteria.<span class="keyword">and</span><span class="constructor">LockNoEqualTo(<span class="params">queryLockNo</span>)</span>; <span class="comment">//基于乐观锁，修改查询版本的数据</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">//根据Example条件更新实体BzFinanceEntity包含的不是null的属性值</span></span><br><span class="line">    <span class="built_in">int</span> mark = this.baseEntityDao.update<span class="constructor">ByExampleSelective(<span class="params">editFinance</span>, <span class="params">example</span>)</span>;</span><br><span class="line">    return mark;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="哈夫曼编码"><a href="#哈夫曼编码" class="headerlink" title="哈夫曼编码"></a><strong>哈夫曼编码</strong></h2><p>哈夫曼编码(Huffman Coding)，又称霍夫曼编码，是一种编码方式，可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法，该方法完全依据字符出现概率来构造异字头的平均长度最短的码字，有时称之为最佳编码，一般就叫做Huffman编码（有时也称为霍夫曼编码）。</p>
<p>哈夫曼编码，主要目的是根据使用频率来最大化节省字符（编码）的存储空间。</p>
<p>霍夫曼编码是一种无前缀编码。解码时不会混淆。其主要应用在数据压缩，加密解密等场合。</p>
<p>如果考虑到进一步节省存储空间，就应该将出现概率大（占比多）的字符用尽量少的0-1进行编码，也就是更靠近根（节点少），这也就是最优二叉树-哈夫曼树。</p>
<p>权值大的在上层，权值小的在下层。满足出现频率高的码长短，即带权路径最小的树, 权值最小的结点远离根结点, 权值越大的结点越靠近根结点</p>
<p>哈夫曼编码的带权路径权值：叶子节点的值 * 叶子节点的高度（根节点为0）</p>
<h2 id="哈夫曼编码结构"><a href="#哈夫曼编码结构" class="headerlink" title="哈夫曼编码结构"></a><strong>哈夫曼编码结构</strong></h2><p>例如要发送一段字符串BCAADDDCCACACAC</p>
<p>由于每个字符占8个比特，上面有15个字符，共占120比特。</p>
<p>现在我们统计每一种字符出现的频率：</p>
<p>A:5</p>
<p>B:1</p>
<p>C:6</p>
<p>D:3</p>
<p>将他们排序后就是B1、D3、A5、C6</p>
<p>然后我们现在创建一个空节点z，将频率最低的字符串放在z左侧、频率第二低的字符串放在z右侧，z赋值为两个字符频率的和。</p>
<p>重复上面的操作，直到所有的字符串都放到二叉树中。</p>
<p>对每一个非叶子节点，将0分配给连接线的左侧，1分配给连接线的右侧。</p>
<p>此时霍夫曼树就构建完成了。</p>
<img src="/wang-cheng/2022/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%85%AD/%E9%9C%8D%E5%A4%AB%E6%9B%BC%E6%A0%91.png" class title="霍夫曼树">
<p>此时A对应的编码为11，B对应编码为100，C对应编码为0，D对应编码为101</p>
<p>而如果不适用霍夫曼编码，字符串的二进制为<br>10000100100001101000001010000010100010001000100010001000100001101000011010000010100001101000001010000110100000101000011</p>
<p>编码后：0000001001011011011111111111</p>
<h2 id="简述mybatis的插件运行原理，如何编写一个插件"><a href="#简述mybatis的插件运行原理，如何编写一个插件" class="headerlink" title="简述mybatis的插件运行原理，如何编写一个插件"></a><strong>简述mybatis的插件运行原理，如何编写一个插件</strong></h2><p>Mybatis只支持针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这四个接口的插件，mybatis使用JDK的动态代理，为需要拦截的接口生成代理对象以实现接口方法拦截功能，每当执行这4种接口对象的方法时，就会进行拦截方法，具体就是InvocationHandler的invoke()方法，拦截那些你指定需要拦截的方法</p>
<h2 id="B-树和B树的区别在哪里-蚂蚁金服"><a href="#B-树和B树的区别在哪里-蚂蚁金服" class="headerlink" title="B+树和B树的区别在哪里(蚂蚁金服)"></a><strong>B+树和B树的区别在哪里(蚂蚁金服)</strong></h2><p>因为B+树非叶子节点是不存储数据的，B树是存储数据的，而且B+树的叶子结点上有所有的数据，而B树的数据是散落在树的各个层级里面，且，B+树的子节点还有指针相连，是一个双向链表、这样，虽然B树的节点数比B+树少，但是应付一些全表扫描的场景上，B+树更有优势；并且，在范围查询的场景中，我们找到了一个值，B+树会更方便顺序取出剩下的所有值。</p>
<p><strong>B树有如下特性：</strong></p>
<p>是一种多路搜索树（并不是二叉的）：</p>
<ol>
<li>定义任意非叶子结点最多只有M个儿子，且M&gt;2；</li>
<li>根结点的儿子数为[2, M]；</li>
<li>除根结点以外的非叶子结点的儿子数为[M/2, M]；</li>
<li>每个结点存放至少M/2-1（取上整）和至多M-1个关键字；（至少2个关键字）</li>
<li>非叶子结点的关键字个数=指向儿子的指针个数-1；</li>
<li>非叶子结点的关键字：K[1], K[2], …, K[M-1]，且K[i] &lt; K[i+1]；</li>
<li>非叶子结点的指针：P[1], P[2], …, P[M]，其中P[1]指向关键字小于K[1]的子树，P[M]指向关键字大于K[M-1]的子树，其它P[i]指向关键字属于(K[i-1], K[i])的子树；</li>
<li>所有叶子结点位于同一层；</li>
</ol>
<img src="/wang-cheng/2022/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%85%AD/B%E6%A0%91.png" class title="B树">
<p>这样就降低了树的高度，也就降低了磁盘的IO次数。但是很遗憾，数据库采用的还不是B树，而是B+树。</p>
<p><strong>B+树的特性：</strong></p>
<p>B+树是B-树的变体，也是一种多路搜索树：</p>
<ol>
<li>其定义基本与B-树同，除了：</li>
<li>非叶子结点的子树指针与关键字个数相同；</li>
<li>非叶子结点的子树指针P[i]，指向关键字值属于[K[i], K[i+1])的子树</li>
<li>B-树是开区间；</li>
<li>为所有叶子结点增加一个链指针；</li>
<li>所有关键字都在叶子结点出现</li>
</ol>
<img src="/wang-cheng/2022/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%85%AD/B+%E6%A0%91.png" class title="B+树">
<p>mysql就是使用这种B+树来存储数据的，所有的数据都在叶子节点，方便做全文检索，且，每次IO的次数都是一样的，比较稳定。</p>
<h2 id="索引的基本原理"><a href="#索引的基本原理" class="headerlink" title="索引的基本原理"></a><strong>索引的基本原理</strong></h2><p>索引用来快速地寻找那些具有特定值的记录。如果没有索引，一般来说执行查询时遍历整张表。<br>索引的原理：就是把无序的数据变成有序的查询</p>
<ol>
<li>把创建了索引的列的内容进行排序</li>
<li>对排序结果生成倒排表</li>
<li>在倒排表内容上拼上数据地址链</li>
<li>在查询的时候，先拿到倒排表内容，再取出数据地址链，从而拿到具体数据</li>
</ol>
<h2 id="mysql聚簇和非聚簇索引的区别"><a href="#mysql聚簇和非聚簇索引的区别" class="headerlink" title="mysql聚簇和非聚簇索引的区别"></a><strong>mysql聚簇和非聚簇索引的区别</strong></h2><p>都是B+树的数据结构</p>
<ul>
<li><p>聚簇索引：将数据存储与索引放到了一块、并且是按照一定的顺序组织的，找到索引也就找到了数据，数据的物理存放顺序与索引顺序是一致的，即：只要索引是相邻的，那么对应的数据一定也是相邻地存放在磁盘上的</p>
</li>
<li><p>非聚簇索引：叶子节点不存储数据、存储的是数据行地址，也就是说根据索引查找到数据行的位置再取磁盘查找数据，这个就有点类似一本树的目录，比如我们要找第三章第一节，那我们先在这个目录里面找，找到对应的页码后再去对应的页码看文章。</p>
</li>
</ul>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">优势：</span><br><span class="line"><span class="number">1</span>、查询通过聚簇索引可以直接获取数据，相比非聚簇索引需要第二次查询（非覆盖索引的情况下）效率要高</span><br><span class="line"><span class="number">2</span>、聚簇索引对于范围查询的效率很高，因为其数据是按照大小排列的 </span><br><span class="line"><span class="number">3</span>、聚簇索引适合用在排序的场合，非聚簇索引不适合 </span><br><span class="line">劣势： </span><br><span class="line"><span class="number">1</span>、维护索引很昂贵，特别是插入新行或者主键被更新导致要分页(page split)的时候。建议在大量插入新行后，选在负载较低的时间段，通过OPTIMIZE <span class="keyword">TABLE</span>优化表，因为必须被移动的行数据可能造成碎片。使用独享表空间可以弱化碎片 </span><br><span class="line"><span class="number">2</span>、表因为使用<span class="type">UUId</span>（随机ID）作为主键，使数据存储稀疏，这就会出现聚簇索引有可能有比全表扫面 更慢，所以建议使用<span class="type">int</span>的auto_increment作为主键 </span><br><span class="line"><span class="number">3</span>、如果主键比较大的话，那辅助索引将会变的更大，因为辅助索引的叶子存储的是主键值；过长的主键值，会导致非叶子节点占用占用更多的物理空间</span><br></pre></td></tr></table></figure>
<p>InnoDB中一定有主键，主键一定是聚簇索引，不手动设置、则会使用unique索引，没有unique索引，则会使用数据库内部的一个行的隐藏id来当作主键索引。在聚簇索引之上创建的索引称之为辅助索引，辅助索引访问数据总是需要二次查找，非聚簇索引都是辅助索引，像复合索引、前缀索引、唯一索引，辅助索引叶子节点存储的不再是行的物理位置，而是主键值</p>
<p>MyISM使用的是非聚簇索引，没有聚簇索引，非聚簇索引的两棵B+树看上去没什么不同，节点的结构完全一致只是存储的内容不同而已，主键索引B+树的节点存储了主键，辅助键索引B+树存储了辅助键。表数据存储在独立的地方，这两颗B+树的叶子节点都使用一个地址指向真正的表数据，对于表数据来说，这两个键没有任何差别。由于索引树是独立的，通过辅助键检索无需访问主键的索引树。</p>
<p>如果涉及到大数据量的排序、全表扫描、count之类的操作的话，还是MyISAM占优势些，因为索引所占空间小，这些操作是需要在内存中完成的。</p>
<h2 id="索引设计的原则？"><a href="#索引设计的原则？" class="headerlink" title="索引设计的原则？"></a><strong>索引设计的原则？</strong></h2><p>查询更快、占用空间更小</p>
<ol>
<li>适合索引的列是出现在where子句中的列，或者连接子句中指定的列</li>
<li>基数较小的表，索引效果较差，没有必要在此列建立索引</li>
<li>使用短索引，如果对长字符串列进行索引，应该指定一个前缀长度，这样能够节省大量索引空间，<br>如果搜索词超过索引前缀长度，则使用索引排除不匹配的行，然后检查其余行是否可能匹配。</li>
<li>不要过度索引。索引需要额外的磁盘空间，并降低写操作的性能。在修改表内容的时候，索引会进<br>行更新甚至重构，索引列越多，这个时间就会越长。所以只保持需要的索引有利于查询即可。</li>
<li>定义有外键的数据列一定要建立索引。</li>
<li>更新频繁字段不适合创建索引</li>
<li>若是不能有效区分数据的列不适合做索引列(如性别，男女未知，最多也就三种，区分度实在太低)</li>
<li>尽量的扩展索引，不要新建索引。比如表中已经有a的索引，现在要加(a,b)的索引，那么只需要修改原来的索引即可。</li>
<li>对于那些查询中很少涉及的列，重复值比较多的列不要建立索引。</li>
<li>对于定义为text、image和bit的数据类型的列不要建立索引。</li>
</ol>
<h2 id="什么是最左前缀原则？什么是最左匹配原则"><a href="#什么是最左前缀原则？什么是最左匹配原则" class="headerlink" title="什么是最左前缀原则？什么是最左匹配原则"></a><strong>什么是最左前缀原则？什么是最左匹配原则</strong></h2><p>在有多索引的数据库中，查询数据时要按照顺序依次查询索引，中间如果跳过某个索引，就不是按照索引进行查找的。</p>
<p>按照索引查找能提高查询速度</p>
<h2 id="锁的类型有哪些"><a href="#锁的类型有哪些" class="headerlink" title="锁的类型有哪些"></a><strong>锁的类型有哪些</strong></h2><p>基于锁的属性分类：共享锁、排他锁。</p>
<p>基于锁的粒度分类：行级锁(INNODB)、表级锁(INNODB、MYISAM)、页级锁(BDB引擎 )、记录锁、间隙锁、临键锁。</p>
<p>基于锁的状态分类：意向共享锁、意向排它锁</p>
<ul>
<li><p>共享锁(Share Lock)</p>
  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">共享锁又称读锁，简称S锁；当一个事务为数据加上读锁之后，其他事务只能对该数据加读锁，而不能对 数据加写锁，直到所有的读锁释放之后其他事务才能对其进行加持写锁。共享锁的特性主要是为了支持 并发的读取数据，读取数据的时候不支持修改，避免出现重复读的问题。</span><br></pre></td></tr></table></figure>
</li>
<li><p>排他锁（Exclusive Lock）</p>
  <figure class="highlight tp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">排他锁又称写锁，简称<span class="keyword">X</span>锁；当一个事务为数据加上写锁时，其他请求将不能再为数据加任何锁，直到该锁释放之后，其他事务才能对数据进行加锁。排他锁的目的是在数据修改时候，不允许其他人同时修改，也不允许其他人读取。避免了出现脏数据和脏读的问题</span><br></pre></td></tr></table></figure>
</li>
<li><p>表锁</p>
  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">表锁是指上锁的时候锁住的是整个表，当下一个事务访问该表的时候，必须等前一个事务释放了锁才能进行对表进行访问； 特点： 粒度大，加锁简单，容易冲突；</span><br></pre></td></tr></table></figure>
</li>
<li><p>行锁</p>
  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">行锁是指上锁的时候锁住的是表的某一行或多行记录，其他事务访问同一张表时，只有被锁住的记录不能访问，其他的记录可正常访问； 特点：粒度小，加锁比表锁麻烦，不容易冲突，相比表锁支持的并发要高；</span><br></pre></td></tr></table></figure>
</li>
<li><p>记录锁(Record Lock)</p>
  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">记录锁也属于行锁中的一种，只不过记录锁的范围只是表中的某一条记录，记录锁是说事务在加锁后锁住的只是表的某一条记录。 精准条件命中，并且命中的条件字段是唯一索引,加了记录锁之后数据可以避免数据在查询的时候被修改的重复读问题，也避免了在修改的事务未提交前被其他事务读取的脏读问题。</span><br></pre></td></tr></table></figure>
</li>
<li><p>页锁</p>
  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快，但冲突多，行级冲突少，但速度慢。所以取了折衷的页级，一次锁定相邻的一组记录。特点：开销和加锁时间界于表锁和行锁之间；会出现死锁；锁定粒度界于表锁和行锁之间，并发度一般</span><br></pre></td></tr></table></figure>
</li>
<li><p>间隙锁(Gap Lock)</p>
  <figure class="highlight excel"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">属于行锁中的一种，间隙锁是在事务加锁后其锁住的是表记录的某一个区间，当表的相邻ID之间出现空隙则会形成一个区间，遵循左开右闭原则。范围查询并且查询未命中记录，查询条件必须命中索引、间隙锁只会出现在REPEATABLE_READ（重复读)的事务级别中。 触发条件：防止幻读问题，事务并发的时候，如果没有间隙锁，就会发生如下图的问题，在同一个事务里，A事务的两次查询出的结果会不一样。比如表里面的数据ID 为 <span class="number">1</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">7</span>,<span class="number">10</span> ,那么会形成以下几个间隙区间，-<span class="built_in">n</span> - <span class="number">1</span>区间，<span class="number">1</span>-<span class="number">4</span>区间，<span class="number">7</span>-<span class="number">10</span> 区间，<span class="number">10</span>-<span class="built_in">n</span>区间 （-<span class="built_in">n</span>代表负无穷大，<span class="built_in">n</span>代表正无穷大）</span><br></pre></td></tr></table></figure>
</li>
<li><p>临建锁(Next-Key Lock)</p>
  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">也属于行锁的一种，并且它是INNODB的行锁默认算法，总结来说它就是记录锁和间隙锁的组合，临键锁会把查询出来的记录锁住，同时也会把该范围查询内的所有间隙空间也会锁住，再之它会把相邻的下一个区间也会锁住。触发条件：范围查询并命中，查询命中了索引。 结合记录锁和间隙锁的特性，临键锁避免了在范围查询时出现脏读、重复读、幻读问题。加了临键锁之 后，在范围区间内数据不允许被修改和插入</span><br></pre></td></tr></table></figure>
<p>如果当事务A加锁成功之后就设置一个状态告诉后面的人，已经有人对表里的行加了一个排他锁<br>了，你们不能对整个表加共享锁或排它锁了，那么后面需要对整个表加锁的人只需要获取这个状态<br>就知道自己是不是可以对表加锁，避免了对整个索引树的每个节点扫描是否加锁，而这个状态就是<br>意向锁。</p>
</li>
<li><p>意向共享锁</p>
  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">当一个事务试图对整个表进行加共享锁之前，首先需要获得这个表的意向共享锁。</span><br></pre></td></tr></table></figure></li>
<li><p>意向排他锁</p>
  <figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">当一个事务试图对整个表进行加排它锁之前，首先需要获得这个表的意向排它锁。</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="InnoDB存储引擎的锁的算法"><a href="#InnoDB存储引擎的锁的算法" class="headerlink" title="InnoDB存储引擎的锁的算法"></a><strong>InnoDB存储引擎的锁的算法</strong></h2><ul>
<li><p>Record lock：单个行记录上的锁</p>
</li>
<li><p>Gap lock：间隙锁，锁定一个范围，不包括记录本身</p>
</li>
<li><p>Next-key lock：record+gap 锁定一个范围，包含记录本身</p>
</li>
</ul>
<p>相关知识点：</p>
<ol>
<li><p>innodb对于行的查询使用next-key lock</p>
</li>
<li><p>Next-locking keying为了解决Phantom Problem幻读问题</p>
</li>
<li><p>当查询的索引含有唯一属性时，将next-key lock降级为record key</p>
</li>
<li><p>Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内，而这会导致幻读问题的产生</p>
</li>
<li><p>有两种方式显式关闭gap锁：（除了外键约束和唯一性检查外，其余情况仅使用record lock） </p>
</li>
</ol>
<p>A.将事务隔离级别设置为RC </p>
<p>B. 将参数innodb_locks_unsafe_for_binlog设置为1</p>
<h2 id="关心过业务系统里面的sql耗时吗？统计过慢查询吗？对查询都怎么优化过？"><a href="#关心过业务系统里面的sql耗时吗？统计过慢查询吗？对查询都怎么优化过？" class="headerlink" title="关心过业务系统里面的sql耗时吗？统计过慢查询吗？对查询都怎么优化过？"></a><strong>关心过业务系统里面的sql耗时吗？统计过慢查询吗？对查询都怎么优化过？</strong></h2><p>在业务系统中，除了使用主键进行的查询，其他的都会在测试库上测试其耗时，慢查询的统计主要由运维在做，会定期将业务中的慢查询反馈给我们。</p>
<p>慢查询的优化首先要搞明白慢的原因是什么？是查询条件没有命中索引？是load了不需要的数据列？还是数据量太大？</p>
<p>所以优化也是针对这三个方向来的，</p>
<ul>
<li><p>首先分析语句，看看是否load了额外的数据，可能是查询了多余的行并且抛弃掉了，可能是加载了许多结果中并不需要的列，对语句进行分析以及重写。</p>
</li>
<li><p>分析语句的执行计划，然后获得其使用索引的情况，之后修改语句或者修改索引，使得语句可以尽可能的命中索引。</p>
</li>
<li><p>如果对语句的优化已经无法进行，可以考虑表中的数据量是否太大，如果是的话可以进行横向或者纵向的分表。</p>
</li>
</ul>
<h2 id="ACID靠什么保证的？"><a href="#ACID靠什么保证的？" class="headerlink" title="ACID靠什么保证的？"></a><strong>ACID靠什么保证的？</strong></h2><p>A原子性由undo log日志保证，它记录了需要回滚的日志信息，事务回滚时撤销已经执行成功的sql</p>
<p>C一致性由其他三大特性保证、程序代码要保证业务上的一致性</p>
<p>I隔离性由MVCC来保证</p>
<p>D持久性由内存+redo log来保证，mysql修改数据同时在内存和redo log记录这次操作，宕机的时候可以从redo log恢复</p>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">InnoDB redo <span class="keyword">log</span> 写盘，InnoDB 事务进入 <span class="keyword">prepare</span> 状态。 如果前面 <span class="keyword">prepare</span> 成功，binlog 写盘，再继续将事务日志持久化到 binlog，如果持久化成功，那么 InnoDB 事务则进入 <span class="keyword">commit</span> 状态(在 redo <span class="keyword">log</span> 里面写一个 <span class="keyword">commit</span> 记录)</span><br></pre></td></tr></table></figure>
<p>redolog的刷盘会在系统空闲时进行</p>
<h2 id="ACID"><a href="#ACID" class="headerlink" title="ACID"></a><strong>ACID</strong></h2><p>A 原子性 要么都执行，要么都不执行 </p>
<p>C 一致性 事物执行前后数据库保持一致的正确状态 </p>
<p>I 隔离性 不同事务之间互不干扰 </p>
<p>D 持久性 事务一旦提交，对数据库中数据的变更是永久的</p>
<h2 id="什么是MVCC"><a href="#什么是MVCC" class="headerlink" title="什么是MVCC"></a><strong>什么是MVCC</strong></h2><p><strong>事务的隔离级别的实现。</strong></p>
<p>多版本并发控制：读取数据时通过一种类似快照的方式将数据保存下来，这样读锁就和写锁不冲突了，不同的事务session会看到自己特定版本的数据，版本链。</p>
<p>MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作。其他两个隔离级别够和MVCC不兼容, 因为 READ UNCOMMITTED 总是读取最新的数据行, 而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁</p>
<p>聚簇索引记录中有两个必要的隐藏列：</p>
<p><strong>trx_id</strong>：用来存储每次对某条聚簇索引记录进行修改的时候的事务id。</p>
<p><strong>roll_pointer</strong>：每次对哪条聚簇索引记录有修改的时候，都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针，它指向这条聚簇索引记录的上一个版本的位置，通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性，因为它没有老版本)</p>
<p><strong>已提交读和可重复读的区别就在于它们生成ReadView的策略不同。</strong></p>
<img src="/wang-cheng/2022/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%85%AD/MVCC.png" class title="MVCC">
<p>开始事务时创建readview，readView维护当前活动的事务id，即未提交的事务id，排序生成一个数组访问数据，获取数据中的事务id（获取的是事务id最大的记录），对比readview：</p>
<p>如果在readview的左边（比readview都小），可以访问（在左边意味着该事务已经提交）</p>
<p>如果在readview的右边（比readview都大）或者就在readview中，不可以访问，获取roll_pointer，取上一版本重新对比（在右边意味着，该事务在readview生成之后出现，在readview中意味着该事务还未提交）</p>
<p>已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView,而可重复读隔离级别则在第一次读的时候生成一个ReadView，之后的读都复用之前的ReadView。</p>
<p>这就是Mysql的MVCC,通过版本链，实现多版本，可并发读-写，写-读。通过ReadView生成策略的不同实现不同的隔离级别。</p>
<p><strong>快照读是基于 MVCC 和 undo log 来实现的，适用于简单 select 语句</strong></p>
<p><strong>当前读是基于 临键锁（行锁 + 间歇锁）来实现的</strong></p>
<h2 id="mysql主从同步原理"><a href="#mysql主从同步原理" class="headerlink" title="mysql主从同步原理"></a><strong>mysql主从同步原理</strong></h2><p>mysql主从同步的过程：</p>
<p>Mysql的主从复制中主要有三个线程： <code>master（binlog dump thread）</code>、<code>slave（I/O thread</code> 、<code>SQL thread）</code> ，Master一条线程和Slave中的两条线程。</p>
<ul>
<li><p>主节点 binlog，主从复制的基础是主库记录数据库的所有变更记录到 binlog。binlog 是数据库服务器启动的那一刻起，保存所有修改数据库结构或内容的一个文件。</p>
</li>
<li><p>主节点 log dump 线程，当 binlog 有变动时，log dump 线程读取其内容并发送给从节点。从节点 I/O线程接收 binlog 内容，并将其写入到 relay log 文件中。</p>
</li>
<li><p>从节点的SQL 线程读取 relay log 文件内容对数据更新进行重放，最终保证主从数据库的一致性</p>
</li>
</ul>
<p>注：主从节点使用 binglog 文件 + position 偏移量来定位主从同步的位置，从节点会保存其已接收到的偏移量，如果从节点发生宕机重启，则会自动从 position 的位置发起同步。(增量同步)</p>
<p>由于mysql默认的复制方式是异步的，主库把日志发送给从库后不关心从库是否已经处理，这样会产生一个问题就是假设主库挂了，从库处理失败了，这时候从库升为主库后，日志就丢失了。由此产生两个概念。</p>
<p><strong>全同步复制</strong></p>
<p>主库写入binlog后强制同步日志到从库，所有的从库都执行完成后才返回给客户端，但是很显然这个方式的话性能会受到严重影响。</p>
<p><strong>半同步复制</strong></p>
<p>和全同步不同的是，半同步复制的逻辑是这样，从库写入日志成功后返回ACK确认给主库，主库收到至少一个从库的确认就认为写操作完成。</p>
<h2 id="简述MyISAM和InnoDB的区别"><a href="#简述MyISAM和InnoDB的区别" class="headerlink" title="简述MyISAM和InnoDB的区别"></a><strong>简述MyISAM和InnoDB的区别</strong></h2><p><strong>MyISAM</strong>：</p>
<p>不支持事务，但是每次查询都是原子的；</p>
<p>支持表级锁，即每次操作是对整个表加锁；</p>
<p>存储表的总行数；</p>
<p>一个MYISAM表有三个文件：索引文件、表结构文件、数据文件；</p>
<p>采用非聚集索引，索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致，但是辅索引不用保证唯一性。</p>
<p>InnoDb：</p>
<p>支持ACID的事务，支持事务的四种隔离级别；</p>
<p>支持行级锁及外键约束：因此可以支持写并发；</p>
<p>不存储总行数；</p>
<p>一个InnoDb引擎存储在一个文件空间（共享表空间，表大小不受操作系统控制，一个表可能分布在多个文件里），也有可能为多个（设置为独立表空，表大小受操作系统文件大小限制，一般为2G），受操作系统文件大小的限制；</p>
<p>主键索引采用聚集索引（索引的数据域存储数据文件本身），辅索引的数据域存储主键的值；因此从辅索引查找数据，需要先通过辅索引找到主键值，再访问辅索引；最好使用自增主键，防止插入数据时，为维持B+树结构，文件的大调整。</p>
<h2 id="简述mysql中索引类型及对数据库的性能的影响"><a href="#简述mysql中索引类型及对数据库的性能的影响" class="headerlink" title="简述mysql中索引类型及对数据库的性能的影响"></a><strong>简述mysql中索引类型及对数据库的性能的影响</strong></h2><p>普通索引：允许被索引的数据列包含重复的值。</p>
<p>唯一索引：可以保证数据记录的唯一性。</p>
<p>主键：是一种特殊的唯一索引，在一张表中只能定义一个主键索引，主键用于唯一标识一条记录，使用关键字 PRIMARY KEY 来创建。</p>
<p>联合索引：索引可以覆盖多个数据列，如像INDEX(columnA, columnB)索引</p>
<p>全文索引：通过建立 倒排索引 ,可以极大的提升检索效率,解决判断字段是否包含的问题，是目前搜索引擎使用的一种关键技术。可以通过ALTER TABLE table_name ADD FULLTEXT (column);创建全文索引</p>
<p>索引可以极大的提高数据的查询速度。</p>
<p>通过使用索引，可以在查询的过程中，使用优化隐藏器，提高系统的性能。</p>
<p>但是会降低插入、删除、更新表的速度，因为在执行这些写操作时，还要操作索引文件索引需要占物理空间，除了数据表占数据空间之外，每一个索引还要占一定的物理空间，如果要建立聚簇索引，那么需要的空间就会更大，如果非聚集索引很多，一旦聚集索引改变，那么所有非聚集索引都会跟着变。</p>
<h2 id="mysql执行计划怎么看"><a href="#mysql执行计划怎么看" class="headerlink" title="mysql执行计划怎么看"></a><strong>mysql执行计划怎么看</strong></h2><p>执行计划就是sql的执行查询的顺序，以及如何使用索引查询，返回的结果集的行数</p>
<p>EXPLAIN SELECT * from A where X=? and Y=?</p>
<img src="/wang-cheng/2022/03/23/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%85%AD/EXPLAIN.png" class title="EXPLAIN">
<ol>
<li><p>id ：是一个有顺序的编号，是查询的顺序号，有几个 select 就显示几行。id的顺序是按 select 出现的顺序增长的。id列的值越大执行优先级越高越先执行，id列的值相同则从上往下执行，id列的值为NULL最后执行。</p>
</li>
<li><p>selectType 表示查询中每个select子句的类型</p>
<ul>
<li><p>SIMPLE： 表示此查询不包含 UNION 查询或子查询</p>
</li>
<li><p>PRIMARY： 表示此查询是最外层的查询（包含子查询）</p>
</li>
<li><p>SUBQUERY： 子查询中的第一个 SELECT</p>
</li>
<li><p>UNION： 表示此查询是 UNION 的第二或随后的查询</p>
</li>
<li><p>DEPENDENT UNION： UNION 中的第二个或后面的查询语句, 取决于外面的查询</p>
</li>
<li><p>UNION RESULT, UNION 的结果</p>
</li>
<li><p>DEPENDENT SUBQUERY: 子查询中的第一个 SELECT, 取决于外面的查询. 即子查询依赖于外层查询的结果.</p>
</li>
<li><p>DERIVED：衍生，表示导出表的SELECT（FROM子句的子查询）</p>
</li>
</ul>
</li>
<li><p>table：表示该语句查询的表</p>
</li>
<li><p>type：优化sql的重要字段，也是我们判断sql性能和优化程度重要指标。他的取值类型范围：</p>
<ul>
<li><p>const：通过索引一次命中，匹配一行数据</p>
</li>
<li><p>system: 表中只有一行记录，相当于系统表；</p>
</li>
<li><p>eq_ref：唯一性索引扫描，对于每个索引键，表中只有一条记录与之匹配</p>
</li>
<li><p>ref: 非唯一性索引扫描,返回匹配某个值的所有</p>
</li>
<li><p>range: 只检索给定范围的行，使用一个索引来选择行，一般用于between、&lt;、&gt;；</p>
</li>
<li><p>index: 只遍历索引树；</p>
</li>
<li><p>ALL: 表示全表扫描，这个类型的查询是性能最差的查询之一。 那么基本就是随着表的数量增多，执行效率越慢。</p>
<p>  <strong>执行效率：</strong><br>  <strong>ALL &lt; index &lt; range&lt; ref &lt; eq_ref &lt; const &lt; system。最好是避免ALL和index</strong></p>
</li>
</ul>
</li>
<li><p>possible_keys：它表示Mysql在执行该sql语句的时候，可能用到的索引信息，仅仅是可能，实际不一定会用到。</p>
</li>
<li><p>key：此字段是 mysql 在当前查询时所真正使用到的索引。 他是possible_keys的子集</p>
</li>
<li><p>key_len：表示查询优化器使用了索引的字节数，这个字段可以评估组合索引是否完全被使用，这也是我们优化sql时，评估索引的重要指标</p>
</li>
<li><p>rows：mysql 查询优化器根据统计信息，估算该sql返回结果集需要扫描读取的行数，这个值相关重要，索引优化之后，扫描读取的行数越多，说明索引设置不对，或者字段传入的类型之类的问题，说明要优化空间越大</p>
</li>
<li><p>filtered：返回结果的行占需要读到的行(rows列的值)的百分比，就是百分比越高，说明需要查询到数据越准确， 百分比越小，说明查询到的数据量大，而结果集很少</p>
</li>
<li><p>extra</p>
<ul>
<li><p>using filesort ：表示 mysql 对结果集进行外部排序，不能通过索引顺序达到排序效果。一般有using filesort都建议优化去掉，因为这样的查询 cpu 资源消耗大，延时大。</p>
</li>
<li><p>using index：覆盖索引扫描，表示查询在索引树中就可查找所需数据，不用扫描表数据文件，往往说明性能不错。</p>
</li>
<li><p>using temporary：查询有使用临时表, 一般出现于排序， 分组和多表 join 的情况， 查询效率不高，建议优化。</p>
</li>
<li><p>using where ：sql使用了where过滤,效率较高。</p>
</li>
</ul>
</li>
</ol>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/22/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%BA%94/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/22/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%BA%94/" class="post-title-link" itemprop="url">面试题(五)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-22 10:55:26" itemprop="dateCreated datePublished" datetime="2022-03-22T10:55:26+08:00">2022-03-22</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-05-03 16:45:59" itemprop="dateModified" datetime="2022-05-03T16:45:59+08:00">2022-05-03</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="Spring事务的实现方式和原理以及隔离级别"><a href="#Spring事务的实现方式和原理以及隔离级别" class="headerlink" title="Spring事务的实现方式和原理以及隔离级别"></a><strong>Spring事务的实现方式和原理以及隔离级别</strong></h2><p>在使用Spring框架时，有两种事务实现方式：一种是编程式，一种是声明式，@Transactiuon注解是声明式。</p>
<p>首先，事务这个概念是数据库层面的，Spring只是基于数据库中的事务进行了扩展，以及提供了一些能让程序员更加方便操作事务的方式</p>
<p>在一个方法上加@Transaction，Spring会基于这个注解生成一个代理对象，将代理对象作为这个类的bean。在使用代理对象时，如果存在这个注解，会将事务的自动提交设置为false(默认是true)，再去执行原本的逻辑方法。如果逻辑方法没有异常，那么代理逻辑会将事务进行提交，如果出现异常，会将事务进行回滚。</p>
<p>当然，针对哪些异常回滚事务是可以配置的，可以利用@Transaction注解中的rollbackFor属性进行配置，默认情况下会对RuntimeException和Error进行回滚。(不会将异常给catch掉)</p>
<p>Spring事务的隔离级别就是数据库的隔离级别：外加一个默认级别</p>
<ul>
<li><p>read uncommitted 未提交读</p>
</li>
<li><p>read committed 提交读、不可重复读</p>
</li>
<li><p>repeatable read 可重复读</p>
</li>
<li><p>serializable 可串行化</p>
</li>
</ul>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">数据库的配置隔离级别是<span class="keyword">Read</span> Commited,而Spring配置的隔离级别是<span class="keyword">Repeatable</span> <span class="keyword">Read</span>，请问这时隔离级别是以哪一个为准</span><br><span class="line">以Spring配置的为准，如果Spring设置的隔离级别数据库不支持，效果取决于数据库</span><br></pre></td></tr></table></figure>
<h2 id="脏读、不可重复读、幻读"><a href="#脏读、不可重复读、幻读" class="headerlink" title="脏读、不可重复读、幻读"></a><strong>脏读、不可重复读、幻读</strong></h2><p><strong>脏读</strong>：一个事务读取了另一个事务还没提交(回滚)的事务</p>
<p><strong>不可重复读</strong>：两次查询同一条数据有不同的结果，因为其他事务可能在<code>UPDATE</code>操作</p>
<p><strong>幻读</strong>：事务B根据条件查询到N条数据，但是这时事务A<code>INSERT</code>或<code>DELETE</code>了M条符合事务B查询条件的数据。事务B再次查询结果就和上一次不一致了，得到了N+M条数据</p>
<p>脏读是select操作，查询到了脏数据</p>
<p>不可重复读通常是其他线程发生了update操作，再次读取结果与上一次不同</p>
<p>幻读通常是其他线程发生了insert 或 delete操作，再次读取结果与上一次不同</p>
<p><strong>丢失更新</strong>：丢失更新指两个事务同时更新一行数据，后提交（或撤销）的事务将之前事务提交的数据覆盖了。</p>
<p>丢失更新可分为两类，分别是第一类丢失更新和第二类丢失更新。</p>
<ul>
<li><p>第一类丢失更新是指两个事务同时操作同一个数据时，当第一个事务撤销时，把已经提交的第二个事务的更新数据覆盖了，第二个事务就造成了数据丢失。</p>
</li>
<li><p>第二类丢失更新是指当两个事务同时操作同一个数据时，第一个事务将修改结果成功提交后，对第二个事务已经提交的修改结果进行了覆盖，对第二个事务造成了数据丢失。</p>
</li>
</ul>
<h2 id="MySQL四种隔离级别"><a href="#MySQL四种隔离级别" class="headerlink" title="MySQL四种隔离级别"></a><strong>MySQL四种隔离级别</strong></h2><p><strong>读未提交(一级封锁协议)</strong>：本质是在事务A中修改完数据M后，立刻对这个数据M加上共享锁(S锁)[当事务A继续修改数据M的时候，先释放掉S锁，再修改数据，再加上S锁]，根据S锁的特性，事务B可以读到事务A修改后的数据(无论事务A是否提交，因为是共享锁，随时随地都能查到数据A修改后的结果)，事务B不能去修改数据M，直到事务A提交，释放掉S锁。</p>
<p>缺点：可能会造成丢失更新、脏读、不可重复读、幻读</p>
<p><strong>读已提交(二级封锁协议)</strong>：本质是事务A在修改数据M后立刻加X锁，事务B不能修改数据M，同时不能查询到最新的数据M(避免脏读)，查询到的数据M是上一个版本(Innodb MVCC快照)的。</p>
<p>优点：避免脏读</p>
<p>缺点：可能会造成丢失更新、不可重复读、幻读</p>
<p><strong>可重复读(三级封锁协议)</strong>：本质是二级封锁协议基础上，对<strong>读到的数据M</strong>瞬间加上共享锁，直到事务结束才释放（保证了其他事务没办法修改该数据），这个级别是MySql 5.5 默认的隔离级别。</p>
<p>优点：避免脏读、不可重复读</p>
<p>缺点：可能会造成丢失更新、幻读。</p>
<p><strong>序列化(最强封锁协议)</strong>：本质是从MVCC并发控制退化到基于锁的并发控制，对事务中所有读取操作加S锁，写操作加X锁，这样可以避免脏读，不可重复读，幻读，更新丢失，开销也最大，会造成读写冲突，并发程度也最低。</p>
<h2 id="Spring事务传播机制"><a href="#Spring事务传播机制" class="headerlink" title="Spring事务传播机制"></a><strong>Spring事务传播机制</strong></h2><p>多个事务相互调用时，事务如何在这些方法间传播<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">方法<span class="selector-tag">A</span>是一个事务方法，方法<span class="selector-tag">A</span>执行过程中调用了方法<span class="selector-tag">B</span>，那么方法<span class="selector-tag">B</span>有无事务以及方法<span class="selector-tag">B</span>对事务的要求不同都会对方法<span class="selector-tag">A</span>的事务具体执行造成影响，同时方法<span class="selector-tag">A</span>的事务对方法<span class="selector-tag">B</span>的事务执行也有影响，这种影响具体是什么就由两个方法所定义的事务传播类型所决定。</span><br></pre></td></tr></table></figure></p>
<p>@Transaction()注解内的可选项</p>
<p>REQUIRED(Spring默认的事务传播类型):如果当前(调用方)没有事务，则自己(被调用方)新建一个事务，如果当前存在事务，则加入这个事务(这个事务内所有SQL要么一起成功，要么一起失败)</p>
<p>SUPPORTS:当前存在事务，则加入当前事务，当前没有事务，就以非事务方法执行</p>
<p>MANDATORY:当前存在事务，则加入当前事务，如果当前没有事务，则抛出异常</p>
<p>REQUIRES_NEW:创建一个新事务，当前存在事务，则挂起该事务(当前的事务挂起，自己的事务先处理，处理话再处理当前的事务。就是自己处理自己的事务)</p>
<p>NOT_SUPPORTED:以非事务方式执行(自己一定是非事务运行，不要事务)，如果当前存在事务，则挂起当前事务(当前的事务，在自己的逻辑处理完成后，再处理)</p>
<p>NEVER:不使用事务，如果当前事务存在，则抛出异常</p>
<p>NESTED:如果当前存在事务，则在嵌套事务中执行，否则REQUIRED的操作一样(开启一个事务)<br><figure class="highlight oxygene"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">和REQUIRES_NEW对比：</span><br><span class="line">REQUIRES_NEW是新建一个事务并且新开启的这个事务与原事务无关。而<span class="keyword">NESTED</span>则是当前存在事务时(我们把当前事务称之为父事务)会开启一个嵌套事务(称之为子事务)。在<span class="keyword">NESTED</span>情况下父事务回滚时，子事务也回滚，而在REQUIRES_NEW情况下，原有事务回滚，不会影响新开启的事务</span><br><span class="line"></span><br><span class="line">和REQUIRED区别：</span><br><span class="line">REQUIRED情况下，调用方存在事务时，则被调用方和调用方使用同一事务，那么被调用方出现异常时，由于共用一个事务，所以无论调用方是否catch其异常，事务都会回滚，而在<span class="keyword">NESTED</span>情况下，被调用方发生异常时，调用方可以catch其异常，这样具有子事务回滚，父事务不受影响</span><br></pre></td></tr></table></figure></p>
<h2 id="Spring事务什么时候会失效"><a href="#Spring事务什么时候会失效" class="headerlink" title="Spring事务什么时候会失效"></a><strong>Spring事务什么时候会失效</strong></h2><p>Spring事务的原理是AOP，进行了切面增强，那么失效的根本原因是这个AOP不起作用了！常见的情况有如下几种：</p>
<p>1、发生自调用，类里面使用this调用本类的方法(this通常省略)，此时这个this对象不是代理类(使用getBean()获取到的才是代理对象)，而是UserService本身！</p>
<p>解决方法很简单，让那个this变成UserService的代理类即可(使用被@Autowire注入的对象去调)</p>
<p>2、方法不是public的</p>
<figure class="highlight aspectj"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Transaction</span>只能用于<span class="keyword">public</span>方法上，否则事务不会失效，如果要用在非<span class="keyword">public</span>上，可以开启AspectJ代理模式</span><br></pre></td></tr></table></figure>
<p>3、数据库不支持事务</p>
<p>4、没有被Spring管理(没有例如@Service这样的注解，没有将这个类放入IOC容器)</p>
<p>5、异常被吃掉(catch)，事务不会回滚(或者抛出的异常没有被定义，默认为RuntimeException)</p>
<h2 id="什么是bean的自动装配，有哪些方式"><a href="#什么是bean的自动装配，有哪些方式" class="headerlink" title="什么是bean的自动装配，有哪些方式"></a><strong>什么是bean的自动装配，有哪些方式</strong></h2><p>自动装配，只需要在xml配置文件<code>&lt;bean&gt;</code>中定义<code>autowire</code>属性<br><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;bean id <span class="operator">=</span> customer class <span class="operator">=</span> <span class="string">&quot;com.xxx.xxx.Customer&quot;</span> autowire<span class="operator">=</span><span class="string">&quot;&quot;</span>/&gt;</span><br></pre></td></tr></table></figure><br><code>autowire</code>属性有五种装配方式</p>
<ul>
<li><p>no - 缺省值装配，自动配置是通过ref属性手动设置的</p>
<figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">手动装配:以value或<span class="built_in">ref</span>的方式明确指定属性值都是手动装配</span><br><span class="line">需要通过<span class="built_in">ref</span>属性来连接bean</span><br></pre></td></tr></table></figure>
</li>
<li><p>byName - 根据bean的属性名称进行自动装配</p>
<figure class="highlight objectivec"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Customer的属性名称为person，Spring会将bean <span class="keyword">id</span>为person的bean通过<span class="keyword">setter</span>方法进行自动装配。</span><br><span class="line">&lt;bean <span class="keyword">id</span> = <span class="string">&quot;person&quot;</span> <span class="keyword">class</span> = <span class="string">&quot;com.xxx.xxx.Person&quot;</span>/&gt;</span><br><span class="line">&lt;bean <span class="keyword">id</span> = <span class="string">&quot;customer&quot;</span> <span class="keyword">class</span> = <span class="string">&quot;com.xxx.xxx.Customer&quot;</span> autowire = <span class="string">&quot;byName&quot;</span>/&gt;</span><br></pre></td></tr></table></figure>
</li>
<li><p>byType - 根据bean类型进行自动装配</p>
<figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Customer的属性person的类型为Person，Spring会将Person类型通过setter方法进行自动装配</span><br><span class="line">&lt;bean id <span class="operator">=</span> <span class="string">&quot;customer&quot;</span> class <span class="operator">=</span> <span class="string">&quot;com.xxx.xxx.Customer&quot;</span> autowire <span class="operator">=</span> <span class="string">&quot;byType&quot;</span>/&gt;</span><br><span class="line">&lt;bean id <span class="operator">=</span> <span class="string">&quot;person&quot;</span> class <span class="operator">=</span> <span class="string">&quot;com.xxx.xxx.Person&quot;</span>/&gt;</span><br></pre></td></tr></table></figure>
</li>
<li><p>constructor - 类似byType，不过是应用于构造器的参数。如果一个bean与构造器参数的类型形同，则进行自动配置，否则导致异常</p>
<figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Custoemr构造函数的参数person的类型为Person，Spring会将Person类型通过构造方法进行自动装配</span><br><span class="line">&lt;bean id <span class="operator">=</span> <span class="string">&quot;customer&quot;</span> class <span class="operator">=</span> <span class="string">&quot;com.xxx.xxx.Customer&quot;</span> autowire <span class="operator">=</span> <span class="string">&quot;constructor&quot;</span>/&gt;</span><br><span class="line">&lt;bead id <span class="operator">=</span> <span class="string">&quot;person&quot;</span> class <span class="operator">=</span> <span class="string">&quot;com.xxx.xxx.Person&quot;</span>/&gt;</span><br></pre></td></tr></table></figure>
</li>
<li><p>autodetect - 如果有默认的构造器，则通过constructor方式进行自动装配，否则使用byType方式进行自动装配</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">如果有默认的构造器，则通过contructor方式进行自动装配，否则使用byType方式进行自动装配</span><br></pre></td></tr></table></figure>
</li>
</ul>
<p>@Autowired自动装配bean，可以在字段、setter方法、构造函数上使用。</p>
<h2 id="Spring-Boot、SpringMVC和Spring有什么区别"><a href="#Spring-Boot、SpringMVC和Spring有什么区别" class="headerlink" title="Spring Boot、SpringMVC和Spring有什么区别"></a><strong>Spring Boot、SpringMVC和Spring有什么区别</strong></h2><p>Spring是一个IOC容器，用来管理bean，使用依赖注入实现控制反转，可以很方便的整合各种框架，提供AOP机制你补OOP的代码重复问题、更方便将不同类不同方法中的共同处理抽取成切面、自动注入给方法执行，比如日志、异常等</p>
<p>SpringMVC是Spring对web框架的一个解决方案，提供了一个总的前端控制器Servlet，用来接收请求，然后定义了一套路由(url和handle的映射)以及适配执行handle，将handle结果使用视图解析技术生成视图展示给前端</p>
<p>SpringBoot是Spring提供的一个快速开发工具包，让程序员能更方便、更快速的开发Spring+SpringMVC应用，简化了配置(约定了默认配置)，整合了一系列的解决方案(starter机制)、redis、mongodb、es，可以开箱即用</p>
<h2 id="Spring-MVC工作流程"><a href="#Spring-MVC工作流程" class="headerlink" title="Spring MVC工作流程"></a><strong>Spring MVC工作流程</strong></h2><ol>
<li><p>用户发送请求至前端控制器DispatcherServlet</p>
</li>
<li><p>DispatcherServlet收到请求调用HanlerMapping处理器映射器(维护url到handle的一个映射关系)</p>
</li>
<li><p>处理器映射器找到具体的处理器(可以根据xml配置、注解查找)，生成处理器以及拦截器(如果有则生成)一并返回给DispatcherServlet</p>
</li>
<li><p>DispatcherServlet调用HandlerAdapter处理器适配器(适配器调用support方法判断Controller类是用什么实现的(实现Controller接口、@Controller、继承Servlet)，根据Controller实现方式不同，进行不同的处理)</p>
</li>
<li><p>HandlerAdapter经过适配调用具体的处理器(Controller，也叫后端控制器)</p>
</li>
<li><p>Controller执行完成返回ModelAndView</p>
</li>
<li><p>HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet</p>
</li>
<li><p>DispatcherServlet将ModelAndView解析后传给ViewResolver视图解析器</p>
</li>
<li><p>ViewResolver解析后返回具体view</p>
</li>
<li><p>DispatcherServlet根据view进行渲染视图(即将模型数据填充至视图中)</p>
</li>
<li><p>DispatcherServlet响应用户</p>
</li>
</ol>
<img src="/wang-cheng/2022/03/22/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%BA%94/SpringMVC%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B.jpg" class title="SpringMVC执行流程">
<h2 id="SpringMVC的主要组件-九大组件"><a href="#SpringMVC的主要组件-九大组件" class="headerlink" title="SpringMVC的主要组件(九大组件)"></a><strong>SpringMVC的主要组件(九大组件)</strong></h2><p>Handler：也就是处理器。它直接对应着MVC中的C，也就是Controller层，它的具体表现形式有很多，可以是类，也可以是方法。在Controller层中@RequestMapping标注的所有地方都可以看成是一个Handler，只要可以实际处理请求就可以是Handler</p>
<p>1、HandlerMapping(维护了url到handler的一个映射关系<url,handler>)</url,handler></p>
<p>initHandlerMappings(context)，处理器映射器，根据用户请求的资源uri来查找Handler的。在SpringMVC中会有很多请求，每个请求都需要一个Handler处理，具体接收到一个请求后使用哪个Handler处理，这就是HandlerMapping需要做的事(DispatcherServlet接到请求以后会遍历处理器映射器，找每一个处理器映射器里面所维护的map<url,handler>，如果找到了相同的url，就证明找到了处理器)</url,handler></p>
<p>2、HandlerAdapter</p>
<p>initHandlerAdapters(context)，适配器。因为SpringMVC中的Handler可以是任意形式的，只要能处理请求就ok，但是Servlet需要的处理方法的结构却是固定的，都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢，这就是HandlerAdapter需要做的事。Handler是用来干活的工具；HandlerMapping用于根据需要干的活找到相应的工具；HandlerAdapter是使用工具的人。</p>
<p>3、HandlerExceptionResolver</p>
<p>initHandlerExceptionResolver(context)，其他组件都是用来干活的。在干活过程中难免会出现问题，出问题后怎么办呢？这就需要一个专门的角色对异常进行处理，在SpringMVC中就是HandlerExceptionResolver，具体来说，此组件的作用是根据异常设置ModelAndView，之后再交给render方法进行渲染</p>
<p>4、ViewResolver</p>
<p>initViewResolver(context)，视图解析器，ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的，也就是将程序返回的参数填入模板里，生成html(也可以是其他文件)文件。这里就有两个关键问题：使用哪个模板？用什么技术(规则)填入参数？这其实是ViewResolver主要要做的工作，ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染，具体的渲染过程则交由不同的视图自己完成。</p>
<p>5、RequestToViewNameTranslator</p>
<p>6、LocaleResolver </p>
<p>SpringMVC主要有两个地方用到了Locale：一是ViewResolver视图解析的时候，二是用到国际化资源或者主题的时候</p>
<p>7、ThemeResolver</p>
<p>用于解析主题 </p>
<p>8、MultipartResolver</p>
<p>用于处理文件上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest，后者可以直接调用getFile方法获取File，如果上传多个文件，还可以调用getFileMap得到FileName—-&gt;File结构的map。一共三个方法，分别是判断是不是上传请求、将request包装成MultipartHttpServletRequest、处理完后清理上传过程中产生的临时资源</p>
<p>9、FlashMapManager</p>
<p>initFlashMapManager(context)，用来管理FlashMap的，FlashMap主要用在redirect中传递参数</p>
<h2 id="Spring-Boot自动配置原理"><a href="#Spring-Boot自动配置原理" class="headerlink" title="Spring Boot自动配置原理"></a><strong>Spring Boot自动配置原理</strong></h2><p>@Import + @Configuration + Spring spi</p>
<p>自动装配类是由各个starter提供，使用@Configuration + @Bean定义配置类，放到META-INF/spring.factories下使用spring spi扫描META-INF/spring.factories下的配置类</p>
<img src="/wang-cheng/2022/03/22/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%BA%94/%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E6%B5%81%E7%A8%8B.jpg" class title="自动配置流程">
<h2 id="如何理解SpringBoot中的Starter"><a href="#如何理解SpringBoot中的Starter" class="headerlink" title="如何理解SpringBoot中的Starter"></a><strong>如何理解SpringBoot中的Starter</strong></h2><p>在使用Spring+SpringMVC开发时，如果需要引入mybatis等框架，需要到xml中定义mybatis需要的bean</p>
<p>starter就是定义一个starter的jar包，写一个@Configuration配置类，将这些bean定义在里面，然后在starter包的META-INF/spring.factories中写入该配置类，springboot会按照约定来加载该配置类</p>
<p>开发人员只需要将相应的starter包依赖进应用，进行相应的属性配置(使用默认配置时，不需要配置)，就可以直接进行代码开发，使用对应的功能，比如mybatis-spring-boot-starter,spring-boot-starter-redis</p>
<h2 id="什么是嵌入式服务器，为什么要使用嵌入式服务器"><a href="#什么是嵌入式服务器，为什么要使用嵌入式服务器" class="headerlink" title="什么是嵌入式服务器，为什么要使用嵌入式服务器"></a><strong>什么是嵌入式服务器，为什么要使用嵌入式服务器</strong></h2><p>节省了下载安装tomcat，应用也不需要再打war包，然后放到webapp目录下再运行</p>
<p>只需要一个安装了java的虚拟机，就可以直接在上面部署应用程序了</p>
<p>springboot已经内置了tomcat.jar，运行main方法时会去启动tomcat，并利用tomcat的spi机制加载springmvc。</p>
<h2 id="Jar包和War包的区别"><a href="#Jar包和War包的区别" class="headerlink" title="Jar包和War包的区别"></a><strong>Jar包和War包的区别</strong></h2><p><strong>什么是jar包?</strong></p>
<p>jar包是类的归档文件，jar文件格式以流行的ZIP文件格式作为基础，和ZIP文件不一样的是，JAR文件不仅仅用来进行压缩和发布，还用来部署和封装库、组件以及插件程序，并且，还能够被像编译器以及JVM这样的工具直接使用。</p>
<p><strong>什么是war包?</strong></p>
<p>war包是JavaWeb程序打的包，war包当中包含写的代码编译成的class文件，依赖的包，配置文件，所有的网站页面，包括html，jsp等等。可以将一个war包理解成一个web项目，里面是项目的所有东西。</p>
<p>目录结构：</p>
<ul>
<li><p>jar包当中的com里面放的是class文件，配置文件，可是没有静态资源的文件,大部分的JAR里面，含有一个META-INF目录，它是用来存储包和扩展的配置数据的，例如，安全性和版本信息。</p>
</li>
<li><p>war包当中的WEB-INF里放的class文件和配置文件，META-INF和jar包作用一样，war包里面还包含静态资源的文件。</p>
</li>
</ul>
<p>jar包和war包的不同之处：</p>
<ul>
<li><p>war包和项目的文件结构保持一致，而jar包却不一样;</p>
</li>
<li><p>jar包里面没有静态资源的文件;</p>
</li>
</ul>
<p>部署项目的区别</p>
<p>部署普通的spring项目用war包就可以，部署springboot项目用jar包就可以，因为springboot内置tomcat。</p>
<h2 id="SSRF"><a href="#SSRF" class="headerlink" title="SSRF"></a><strong>SSRF</strong></h2><p>概念：SSRF为服务端请求伪造(Server-Side Request Forgery),指的是攻击者在未能取得服务器所有权限时，利用服务器漏洞以服务器的身份发送一条构造好的请求给服务器所在内网。SSRF攻击通常针对外部网络无法直接访问的内部系统。 </p>
<p>原理：很多web应用都提供了从其他的服务器上获取数据的功能。使用指定的URL，web应用便可以获取图片，下载文件，读取文件内容等。SSRF的实质是利用存在缺陷的web应用作为代理攻击远程和本地的服务器。一般情况下， SSRF攻击的目标是外网无法访问的内部系统，黑客可以利用SSRF漏洞获取内部系统的一些信息（正是因为它是由服务端发起的，所以它能够请求到与它相连而与外网隔离的内部系统）。SSRF形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。</p>
<p>主要攻击方式：攻击者想要访问主机B上的服务，但是由于存在<em>*</em>或者主机B是属于内网主机等原因导致攻击者无法直接访问主机B。而服务器A存在SSRF漏洞，这时攻击者可以借助服务器A来发起SSRF攻击，通过服务器A向主机B发起请求，从而获取主机B的一些信息。</p>
<h2 id="CSRF"><a href="#CSRF" class="headerlink" title="CSRF"></a><strong>CSRF</strong></h2><p>跨站请求伪造（英语：Cross-site request forgery），也被称为 one-click attack 或者 session riding，通常缩写为 CSRF或者 XSRF， 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨网站脚本（XSS）相比，XSS 利用的是用户对指定网站的信任，CSRF 利用的是网站对用户网页浏览器的信任。 </p>
<h2 id="AOP中术语描述"><a href="#AOP中术语描述" class="headerlink" title="AOP中术语描述"></a><strong>AOP中术语描述</strong></h2><ol>
<li><p>连接点（Joinpoint）:连接点描述的是程序执行的某个特定位置。如一个类的初始化前、初始化后，或者类的某个方法调用前、调用后、方法抛出异常后等等。一个类或一段程序代码拥有一些具有边界性质的特定点，这些特定点就称为连接点。连接点用来定义在目标程序的哪里通过AOP加入新的逻辑。Spring仅支持方法的连接点，即仅能在方法调用前、方法调用后、方法抛出异常时以及方法调用前后这些程序执行点织入增强。<br>连接点由两个信息确定：第一是用方法表示的程序执行点；第二是用相对点表示的方位。<br>执行点：即程序执行流程中的某个点。如执行某个语句或者语句块、执行某个方法、装载某个类、抛出某个异常……</p>
</li>
<li><p>切入点（Pointcut）:切入点是一个连接点的过滤条件，AOP 通过切点定位到特定的连接点。每个类都拥有多个连接点：例如 UserService类中的所有方法实际上都是连接点，即连接点是程序类中客观存在的事物。类比：连接点相当于数据库中的记录，切点相当于查询条件。切点和连接点不是一对一的关系，一个切点匹配多个连接点，切点通过 org.springframework.aop.Pointcut 接口进行描述，它使用类和方法作为连接点的查询条件。</p>
</li>
<li><p>通知（Advice）:切面在某个具体的连接点采取的行为或行动，称为通知。切面的核心逻辑代码都写在通知中，有人也称之为增强或者横切关注点。通知是切面功能的具体实现，通常是业务代码以外的需求，如日志、验证等，这些被模块化的特殊对象。常用的通知接口有:</p>
<ul>
<li><p>前置通知：org.springframework.aop.MethodBeforeAdvice</p>
</li>
<li><p>后置通知：org.springframework.aop.AfterReturningAdvice</p>
</li>
<li><p>异常通知：org.springframework.aop.ThrowsAdvice</p>
</li>
<li><p>该接口没有要实现的方法，需要自定义一个afterThrowing()方法。</p>
</li>
<li><p>环绕通知：org.aopalliance.intercept.MethodInterceptor</p>
</li>
</ul>
</li>
<li><p>通知器（Advisor）:通知器由一个切入点（pointcut）和一个通知（Advice）组成。通知就是增强的那部分功能代码，如记录日志代码、控制权限代码。</p>
</li>
<li><p>切面（Aspect）:与通知器（advisor）类似都是通知+切入点。区别在于，切面中的类无需实现通知接口，但需要在配置文件中指定类中的方法名；而通知器仅需指定类名即可，因为通知器中的类都实现了通知接口，很明确的知道通知方法是哪个。</p>
</li>
<li><p>目标(Target):被通知的对象（方法）</p>
</li>
<li><p>代理(Proxy):向目标对象应用通知之后创建的对象</p>
</li>
</ol>
<h2 id="GC中的新生代、老年代垃圾收集器"><a href="#GC中的新生代、老年代垃圾收集器" class="headerlink" title="GC中的新生代、老年代垃圾收集器"></a><strong>GC中的新生代、老年代垃圾收集器</strong></h2><img src="/wang-cheng/2022/03/22/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%BA%94/%E6%96%B0%E7%94%9F%E4%BB%A3%E5%92%8C%E8%80%81%E5%B9%B4%E4%BB%A3GC.png" class title="新生代和老年代GC">
<h2 id="Java程序初始化三个原则"><a href="#Java程序初始化三个原则" class="headerlink" title="Java程序初始化三个原则"></a><strong>Java程序初始化三个原则</strong></h2><ol>
<li><p>静态对象（变量）优先于非静态对象（变量）初始化，其中静态对象（变量）初始化一次，非静态对象（变量）可能会初始化多次。</p>
</li>
<li><p>父类优先于子类初始化</p>
</li>
<li><p>按照成员变量定义顺序初始化，即使变量定义与方法定义中，依然在被调用前初始化。</p>
</li>
</ol>
<h2 id="Java初始化-类加载-顺序"><a href="#Java初始化-类加载-顺序" class="headerlink" title="Java初始化(类加载)顺序"></a><strong>Java初始化(类加载)顺序</strong></h2><ol>
<li><p>父类静态变量</p>
</li>
<li><p>父类静态代码块</p>
</li>
<li><p>子类静态变量</p>
</li>
<li><p>子类静态代码块</p>
</li>
<li><p>父类非静态变量</p>
</li>
<li><p>父类非静态代码块</p>
</li>
<li><p>父类构造函数</p>
</li>
<li><p>子类非静态变量</p>
</li>
<li><p>子类非静态代码块</p>
</li>
<li><p>子类构造函数</p>
</li>
</ol>
<h2 id="java类的初始化和实例化区别"><a href="#java类的初始化和实例化区别" class="headerlink" title="java类的初始化和实例化区别"></a><strong>java类的初始化和实例化区别</strong></h2><p>类的初始化：是完成程序执行前的准备工作。在这个阶段，静态的（变量，方法，代码块）会被执行。同时在会开辟一块存储空间用来存放静态的数据。<strong>初始化只在类加载的时候执行一次</strong>。</p>
<p>类的实例化：是指创建一个对象的过程。这个过程中会在堆中开辟内存，将一些非静态的方法，变量存放在里面。在程序执行的过程中，可以创建多个对象，既多次实例化。每次实例化都会开辟一块新的内存。</p>
<h2 id="TCP发送报文发生粘包问题解决方案"><a href="#TCP发送报文发生粘包问题解决方案" class="headerlink" title="TCP发送报文发生粘包问题解决方案"></a><strong>TCP发送报文发生粘包问题解决方案</strong></h2><ol>
<li><p>发送固定长度的消息</p>
</li>
<li><p>包结尾增加分隔符</p>
</li>
<li><p>把消息分成消息头和消息体，其中消息头上包含长度</p>
</li>
</ol>
<h2 id="最大公约数和最小公倍数"><a href="#最大公约数和最小公倍数" class="headerlink" title="最大公约数和最小公倍数"></a><strong>最大公约数和最小公倍数</strong></h2><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> a,b;</span><br><span class="line"><span class="keyword">int</span> small = a&lt;b?a:b;<span class="comment">//a和b的最小数</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> i=small;i&gt;=<span class="number">1</span>;i--) &#123;</span><br><span class="line">    <span class="keyword">if</span>(a%i==<span class="number">0</span> &amp;&amp; b%i==<span class="number">0</span>) &#123;</span><br><span class="line">        System.out.<span class="keyword">println</span>(<span class="string">&quot;最大公约数：&quot;</span>+i);</span><br><span class="line">        System.out.<span class="keyword">println</span>(<span class="string">&quot;最小公倍数：&quot;</span>+(i*(a<span class="regexp">/i)*(b/i</span>)));</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/20/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9B%9B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/20/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9B%9B/" class="post-title-link" itemprop="url">面试题(四)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-20 17:15:06" itemprop="dateCreated datePublished" datetime="2022-03-20T17:15:06+08:00">2022-03-20</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-05-02 15:58:40" itemprop="dateModified" datetime="2022-05-02T15:58:40+08:00">2022-05-02</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="并发、并行、串行的区别"><a href="#并发、并行、串行的区别" class="headerlink" title="并发、并行、串行的区别"></a><strong>并发、并行、串行的区别</strong></h2><p>串行在时间上不可重叠，前一个任务没搞定，下一个任务只能等着</p>
<p>并行在时间上重叠，两个任务在同一时刻互不干扰的同时执行</p>
<p>并发允许两个任务彼此干扰。统一时间点、只有一个任务运行，交替执行</p>
<h2 id="并发的三大特性"><a href="#并发的三大特性" class="headerlink" title="并发的三大特性"></a><strong>并发的三大特性</strong></h2><p>缓存导致了可见性问题，线程切换带来了原子性问题，编译优带来了有序性问题</p>
<ul>
<li>原子性</li>
</ul>
<p>原子性是指在一个操作中cpu不可以在中途暂停然后再调度，即不被中断操作，要不全部执行完成，要不都不执行。</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">i++</span><br><span class="line"><span class="bullet">1.</span> 将i从主存读到工作内存中的副本中</span><br><span class="line"><span class="bullet">2.</span> +1的运算</span><br><span class="line"><span class="bullet">3.</span> 将结果写入工作内存</span><br><span class="line">原子性就是指这三步操作要么一起执行，要么不执行，中间不可以中断</span><br></pre></td></tr></table></figure>
<p>synchronized关键字可以保证原子性</p>
<ul>
<li>可见性</li>
</ul>
<p>当多个线程访问同一个变量时，一个线程修改了这个变量的值，其他线程能够立刻看到修改的值</p>
<figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="bullet">4.</span> 将工作内存的值刷回到主存(什么时候刷入由操作系统决定，不确定的)</span><br><span class="line">可见性就是指第4步与第3步操作要么一块执行，要么不执行，中间不可以中断</span><br></pre></td></tr></table></figure>
<p>volatile、synchronized、final都可以保证可见性</p>
<ul>
<li>有序性</li>
</ul>
<p>虚拟机在进行代码编译时，对于那些改变顺序后不会对最终结果造成影响的代码，虚拟机不一定会按照我们写的代码的顺序来执行，有可能将他们重排序。实际上，对于有些代码进行重排序后，虽然对变量的值没有造成影响，但有可能会出现线程安全问题。<br><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">int</span> a = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">bool</span> flag = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">write</span>()</span>&#123;</span><br><span class="line">    a = <span class="number">2</span>;</span><br><span class="line">    flag = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">multiply</span>()</span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(flag)&#123;</span><br><span class="line">        <span class="built_in">int</span> ret = a * a;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>volatile、synchronized都可以保证有序性</p>
<p>write方法里的1和2做了重排序，线程1先对flag赋值为true，随后执行到线程2，ret直接计算出结果，再到线程1，这时候a才赋值为2，明显迟了一步。</p>
<p>volatile本身就包含了禁止指令重排序的语义，而synchronized关键字是由”一个变量在同一时刻只允许一条线程对其进行lock操作”这条规则明确的</p>
<p>synochronized关键字同时满足以上三种特性，但volatile不满足原子性。</p>
<p>在某些情况下，volatile的同步机制的性能确实要优于锁(使用synchronized关键字或java.util.concurrent包里面的锁)，因为volatile的总开销要比锁低。</p>
<h2 id="为什么要用线程池？解释下线程池参数？"><a href="#为什么要用线程池？解释下线程池参数？" class="headerlink" title="为什么要用线程池？解释下线程池参数？"></a><strong>为什么要用线程池？解释下线程池参数？</strong></h2><ol>
<li><p>降低资源消耗；提高线程利用率，降低创建和销毁线程的消耗</p>
</li>
<li><p>提高效应速度；任务来了，直接有线程可用可执行，而不是先创建线程，再执行</p>
</li>
<li><p>提高线程的可管理性；线程是稀缺资源，使用线程池可以统一分配调优监控</p>
<ul>
<li><p><code>corePoolSize</code>代表核心线程数，也就是正常情况下创建工作的线程数，这些线程创建后并不会消除，而是一种常驻线程(随着线程池的回收一起回收掉)</p>
</li>
<li><p><code>maxinumPoolSize</code>代表的是最大线程数，它与核心线程数相对应，表示最大允许被创建的线程数，比如当前任务较多，将核心线程数都用完了，还无法满足需求时，此时就会创建新的线程，但是线程池内线程总数不会超过最大线程数</p>
</li>
<li><p><code>KeepAliveTime、unit</code>表示超过核心线程数之外的线程的空闲存活时间，也就是核心线程不会消除，但是超过核心线程数的部分线程如果空闲一定时间则会被清除，我们可以通过setKeepAliveTime来设置空闲时间</p>
</li>
<li><p><code>WorkQueue</code>用来存放待执行的任务，假设我们现在核心线程都已被使用，还有任务进来则全部放入队列(不会立即创建新的线程)，直到整个队列都被放满任务还再持续进入则会开始创建新的线程</p>
</li>
<li><p><code>ThreadFactory</code>实际上是一个线程工厂，用来生产线程执行任务。我们可以选择使用默认的创建工厂，产生的线程都会在同一个组内，拥有相同的优先级，且都不是守护线程。当然我们也可以选择自定义线程工厂，一般我们会根据业务来指定不同的线程工厂</p>
</li>
<li><p><code>Handler</code>任务拒绝策略，有两种情况，第一种是当我们调用shutdown等方法关闭线程池后，这时候即使线程池内部还有没执行完的任务在执行，但是由于线程池已经关闭，我们再继续向线程池提交任务就会遭到拒绝。另一种情况就是当达到最大线程数，线程池已经没有能力继续处理新提交的任务时，这是也会拒绝</p>
<ul>
<li><p>CallerRunsPolicy - 当触发拒绝策略，只要线程池没有关闭的话，则使用调用线程直接运行任务。一般并发比较小，性能要求不高，不允许失败。但是，由于调用者自己运行任务，如果任务提交速度过快，可能导致程序阻塞，性能效率上必然的损失较大</p>
</li>
<li><p>AbortPolicy - 丢弃任务，并抛出拒绝执行 RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常，否则会打断当前的执行流程，影响后续的任务执行。</p>
</li>
<li><p>DiscardPolicy - 直接丢弃，其他啥都没有</p>
<p>-DiscardOldestPolicy - 当触发拒绝策略，只要线程池没有关闭的话，丢弃阻塞队列 workQueue 中最老的一个任务，并将新任务加入</p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<h2 id="简述线程池处理流程"><a href="#简述线程池处理流程" class="headerlink" title="简述线程池处理流程"></a><strong>简述线程池处理流程</strong></h2><img src="/wang-cheng/2022/03/20/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9B%9B/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B.png" class title="线程池执行流程">
<h2 id="线程池中阻塞队列的作用？为什么是先添加队列而不是先创建最大线程"><a href="#线程池中阻塞队列的作用？为什么是先添加队列而不是先创建最大线程" class="headerlink" title="线程池中阻塞队列的作用？为什么是先添加队列而不是先创建最大线程"></a><strong>线程池中阻塞队列的作用？为什么是先添加队列而不是先创建最大线程</strong></h2><p>1、 一般的队列只能保证作为一个有限长度的缓冲区，如果超出了缓冲长度，就无法保留当前的任务了，阻塞队列通过阻塞可以保留住当前想要继续入队的任务。</p>
<p>阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程，使得线程进入wait状态，释放cpu资源。</p>
<p>阻塞队列自带阻塞和唤醒的功能，不需要额外处理，无任务执行时，线程池利用阻塞队列的take方法挂起，从而维持核心线程的存活、不至于一直占用cpu资源。</p>
<p>2、 在创建新线程的时候，是要获取全局锁的，这个时候其他的就得阻塞，影响了整体效率</p>
<p>就好比一个企业里面有10个(core)正式工的名额，最多招10个正式工，要是任务超过正式工人数(task&gt;core)的情况下，工厂领导(线程池)不是首先扩招工人，还是这10个人，但是任务可以稍微积压一下，即先放到队列去(代价低)。10个正式工慢慢干，迟早会干完的，要是任务还在继续增加，超过正式工的加班忍耐极限了(队列满了)，就得招外包帮忙了(注意是临时供)，要是正式工加上外包还是不能完成任务，那新来得任务就会被领导拒绝了(线程池的拒绝策略)</p>
<h2 id="线程池中线程复用原理"><a href="#线程池中线程复用原理" class="headerlink" title="线程池中线程复用原理"></a><strong>线程池中线程复用原理</strong></h2><p>线程池将线程和任务进行解耦，线程是线程，任务是任务，摆脱了之前通过Thread创建线程时的一个线程必须对应一个任务的限制。</p>
<p>在线程池中，同一个线程可以从阻塞队列中不断获取新任务来执行，其核心原理在于线程池对Thread进行了封装，并不是每次执行任务都会调用Thread.start()来创建新线程(不使用线程池的话，是用一个任务类去继承Thread或实现Runnable接口，将任务逻辑写在run方法内)，而是让每个线程去执行一个”循环任务”，在这个”循环任务”中不停检查是否有任务需要被执行，如果有则直接执行，也就是调用任务中的run方法(直接调run方法而不是调用start方法)，将run方法当成一个普通的方法执行，通过这种方式只使用固定的线程就将所有任务的run方法串联起来。</p>
<p>理解：真正的任务逻辑，并不是写在线程池中的run方法内，而是利用这些线程去调我任务里的这个run方法，从而执行我的任务逻辑。</p>
<h2 id="Spring是什么"><a href="#Spring是什么" class="headerlink" title="Spring是什么"></a><strong>Spring是什么</strong></h2><p>轻量级的开源的J2EE框架。它是一个容器框架，用来装javabean，中间层框架可以起到连接作用，比如说把Struts和hibernate粘合在一起运用，可以让我们的企业开发更快、更简洁</p>
<p>Spring是一个轻量级的控制反转和面向切面的容器框架</p>
<ul>
<li><p>从大小与开销两方面而言Spring都是轻量级的</p>
</li>
<li><p>通过控制反转的技术达到松耦合的目的</p>
</li>
<li><p>提供了面向切面编程的丰富支持，允许通过分离应用的业务逻辑与系统级服务进行内聚性(采用AOP的切面，专门用来做日志的工作。如果没有使用Spring，那么就需要在代码的许多地方都写入日志性的代码)的开发</p>
</li>
<li><p>包含并管理应用对象的配置和生命周期，这个意义上是一个容器</p>
</li>
<li><p>将简单的组件配置、组合成为复杂的应用(整合功能)，这个意义上是一个框架</p>
</li>
</ul>
<h2 id="对AOP的理解"><a href="#对AOP的理解" class="headerlink" title="对AOP的理解"></a><strong>对AOP的理解</strong></h2><p>系统是由许多不同的组件所组成的，每一个组件各负责一块特定功能。除了实现自身核心功能之外，这些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心业务逻辑的组件中去。这些系统服务经常被称为横切关注点，因为他们会跨越系统的多个组件。</p>
<p>当我们需要为分散的对象引入公共行为的时候，OOP则显得无能为力。也就是说，OOP允许你定义从上到下的关系，但并不适合定义从左到右的关系，例如日志功能。</p>
<p>日志代码往往水平地分散在所有对象层次中，而与它所散布到的对象的核心功能毫无关系。</p>
<p>在OOP设计中，它导致了大量代码的重复，而不利于各个模块的重用。</p>
<p>AOP：将程序中的交叉业务逻辑(比如日志、安全、事务等)，封装成一个切面，然后注入到目标对象中(具体业务逻辑)。AOP可以对某些对象或某个对象的功能进行增强，比如对象中的方法进行增强，可以在执行某个方法之前额外的做一些事，在某个方法执行之后额外做一些事情。</p>
<h2 id="对IOC的理解"><a href="#对IOC的理解" class="headerlink" title="对IOC的理解"></a><strong>对IOC的理解</strong></h2><p>容器概念、控制反转、依赖注入</p>
<p>IOC容器：实际上就是个map(key,value),里面存放的是各种对象(在xml里配置的bean节点、@Repository、@Service、@Controoler、@Component)，在项目启动的时候会读取配置文件里面的bean节点，根据全限定类名使用反射创建对象放在map里、扫描到打上上述注解的类，还是通过反射创建对象放到map中。</p>
<p>这个时候map里就有各种对象了，接下来我们在代码里需要用到里面的对象时，再通过DI注入(@Autowired、@Resource等注解，xml里bean节点内的ref属性，项目启动的时候会读取xml节点ref属性根据id注入，也会扫描这些注解，根据类型或id注入，id就是对象名)</p>
<p><strong>控制反转：</strong></p>
<p>没有引入IOC容器之前，对象A依赖于对象B，那么对象A在初始化或运行到某一点的时候，自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B，控制权都在自己手上。</p>
<p>引入IOC容器后，对象A与对象B之间失去了直接联系；当对象A运行到需要对象B的时候，IOC容器会主动去创建一个对象B注入到对象A需要的地方。</p>
<p>通过对比，不难看出对象A获得依赖对象B的过程，由主动行为变为了被动行为，控制权颠倒了，这就是”控制反转”这个名称的由来。</p>
<p>全部对象的控制权全部上缴给”第三方”IOC容器，所以，IOC容器成了整个系统的关键核心，它起到了一种类似”粘合剂”的作用，把系统中的所有对象粘合在一起发挥作用，如果没有这个”粘合剂”，对象与对象之间会彼此失去联系，这就是有人把IOC容器比喻成”粘合剂”的由来</p>
<p><strong>依赖注入：</strong></p>
<p>“获得依赖对象的过程被反转了”。控制被反转后，获得依赖对象地过程由自身管理变成了由IOC容器主动注入。依赖注入是实现IOC的方法，就是由IOC容器在运行期间，动态地将某种依赖关系注入到对象之中</p>
<h2 id="BeanFactory和ApplicationContext有什么区别"><a href="#BeanFactory和ApplicationContext有什么区别" class="headerlink" title="BeanFactory和ApplicationContext有什么区别"></a><strong>BeanFactory和ApplicationContext有什么区别</strong></h2><p>ApplicationContext是BeanFactory的子接口</p>
<p>ApplicationContext提供了更完整的功能(拓展了BeanFactory)：</p>
<ol>
<li><p>继承MessageSource，因此支持国际化</p>
</li>
<li><p>统一的资源文件访问方式</p>
</li>
<li><p>提供在监听器中注册bean的事件</p>
</li>
<li><p>同时加载多个配置文件</p>
</li>
<li><p>载入多个(有继承关系)上下文，使得每一个上下文都专注于一个特定的层次，比如应用的web层</p>
</li>
</ol>
<ul>
<li><p>BeanFactory采用的是延迟加载形式来注入Bean的，即只有在使用到某个Bean时(调用getBean())，才对该Bean进行加载实例化。这样，我们就不能发现一些存在的Spring的配置问题。如果Bean的某一属性没有注入，BeanFactory加载后，直至第一次使用调用getBean()方法才抛出异常</p>
</li>
<li><p>ApplicationContext，它是在容器启动时，一次性创建了所有Bean。这样，在容器启动时，我们就可以发现Spring中存在的配置错误，这样就有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean，通过预载入单实例Bean，确保当你需要的时候，你就不用等待，因为他们已经创建好了。</p>
</li>
<li><p>相对于基本的BeanFactory，ApplicationContext唯一的不足是占用内存空间。当应用配置Bean较多时，程序启动较慢</p>
</li>
<li><p>BeanFactory通常以编程的方式被创建，ApplicationContext还能以声明的方式被创建，如使用ContextLoader</p>
</li>
<li><p>BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor(后置处理器)的使用，但两者的区别是：Beanfactory需要手动注册，而ApplicationContext则是自动注册</p>
</li>
</ul>
<h2 id="描述一下Spring-Bean的生命周期"><a href="#描述一下Spring-Bean的生命周期" class="headerlink" title="描述一下Spring Bean的生命周期"></a><strong>描述一下Spring Bean的生命周期</strong></h2><p>1、解析类得到BeanDefinition</p>
<p>2、 如果有多个构造方法，则要推断构造方法</p>
<p>3、 确定好构造方法后，进行实例化得到一个对象</p>
<p>4、 对对象中的加了@Autowired注解的属性进行填充</p>
<p>5、 回调Aware方法，比如BeanNameAware，BeanFactoryAware</p>
<p>6、 调用BeanPostProcessor的初始化前的方法</p>
<p>7、调用初始化方法</p>
<p>8、 调用BeanPostProcessor的初始化后的方法，在这里会进行AOP</p>
<p>9、 如果当前创建的bean是单例则会把bean放入单例池</p>
<p>10、 使用bean</p>
<p>11、 Spring容器关闭时调用disposableBean中的destory()方法</p>
<h2 id="解释下Spring支持的几种bean的作用域"><a href="#解释下Spring支持的几种bean的作用域" class="headerlink" title="解释下Spring支持的几种bean的作用域"></a><strong>解释下Spring支持的几种bean的作用域</strong></h2><ul>
<li><p>singleton：默认，每个容器中只有一个bean的实例，单例的模式由BeanFactory自身来维护。该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)</p>
</li>
<li><p>prototype：为每一个bean请求提供一个实例。在每次注入时都会创建一个新的对象</p>
</li>
<li><p>request：bean被定义为在每个HTTP请求中创建一个单例对象，也就是说在单个请求中都会复用这一个单例对象。</p>
</li>
<li><p>session：与request范围类似，确保每个session中有一个bean的实例，在session过期后，bean会随之失效</p>
</li>
<li><p>application：bean被定义为在ServletContext(Servlet容器)的生命周期中复用一个单例对象</p>
</li>
<li><p>websocket：bean被定义为在websocket的生命周期中复用一个单例对象</p>
</li>
<li><p>global-session：全局作用域，global-session和Portlet应用相关(只与其有关)。当你的应用部署在Portlet容器中工作时，它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话，那么这个全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。</p>
</li>
</ul>
<h2 id="Spring框架中的单例Bean是线程安全的吗"><a href="#Spring框架中的单例Bean是线程安全的吗" class="headerlink" title="Spring框架中的单例Bean是线程安全的吗"></a><strong>Spring框架中的单例Bean是线程安全的吗</strong></h2><p><strong>不安全</strong></p>
<p>Spring中的Bean默认是单例模式的，框架并没有对bean进行多线程的封装处理。</p>
<p>如果Bean是有状态的，那就需要开发人员自己来进行线程安全的保证，最简单的办法就是改变Bean的作用域，把singleton改为prototyte，这样每次请求Bean就相当于是new Bean()，这样就保证线程安全了。</p>
<ul>
<li><p>有状态就是有数据存储功能</p>
</li>
<li><p>无状态就是不会保存数据，controller、service和dao层本身并不是线程安全的，只是如果只是调用里面的方法，而且多线程调用一个实例的方法，会在内存中复制变量，这是自己的线程的工作内存，是安全的。</p>
</li>
</ul>
<p>Dao会操作数据库Connection，Connection是带有状态的，比如说数据库事务，Spring的事务管理器使用ThreadLocal为不同线程维护了一套独立的Connection副本，保证线程之间不会互相影响(Spring是如何保证事务获取到同一个Connection的)</p>
<p>不要在bean中声明任何有状态的实例变量或类变量，如果必须如此，那么就使用ThreadLocal把变量变为线程私有，如果bean的实例变量或类变量需要在多个线程之间共享，那么就只能使用synchronized、local、CAS等这些实现线程同步的方法了。</p>
<h2 id="Spring框架中都用了哪些设计模式"><a href="#Spring框架中都用了哪些设计模式" class="headerlink" title="Spring框架中都用了哪些设计模式"></a><strong>Spring框架中都用了哪些设计模式</strong></h2><p>简单工厂：由一个工厂类根据传入的参数，动态决定应该创建哪一个产品类<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Spring中的BeanFactory就是简单工厂模式的体现，根据传入一个唯一的标识来获得Bean对象，但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定</span><br></pre></td></tr></table></figure></p>
<p>工厂方法：<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">实现了FactoryBean接口得bean是一类叫做factory得bean。其特点是，spring会在使用<span class="built_in">getBean</span>()调用获得该bean时，会自动调用该bean的<span class="built_in">getObject</span>()方法，所以返回的不是factory的这个bean，而是这个bean<span class="selector-class">.getObject</span>()方法的返回值(这里的返回值可能是FactoryBean的bean对象，也可能是其他对象，这完全取决于你在实现它的bean.getObject方法中返回的什么)</span><br></pre></td></tr></table></figure></p>
<p>单例模式：保证一个类仅有一个实例，并提供一个访问它的全局访问点<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Spring对单例的实现：Spring中的单例模式完成了后半句话，即提供了全局的访问点BeanFactory。但没有从构造器级别去控制单例，这是因为Spring管理的是任意的java对象</span><br></pre></td></tr></table></figure></p>
<p>适配器模式：<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Spring定义了一个适配接口，使得每一种Controller有一种对应的适配器实现类，让适配器代替Controller执行相应的方法。这样在扩展Controller时，只需要增加一个适配器类就完成了SpringMVC的扩展了</span><br></pre></td></tr></table></figure></p>
<p>装饰器模式：动态地给一个对象添加一些额外的职责。就增强功能来说，Decorator模式相比生成子类更为灵活<br><figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Spring中用到的包装器模式在类名上有两种表现：一种是类名中含有<span class="keyword">Wrapper</span>，另一种是类名中含有Decorator</span><br></pre></td></tr></table></figure></p>
<p>动态代理：<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">切面在应用运行的时刻被织入。一般情况下，在织入切面时，AOP容器会为目标对象创建动态的代理对象。SpringAOP就是以这种方式织入切面的。</span><br><span class="line"></span><br><span class="line">织入：把切面应用到目标对象并创建新的代理对象的过程</span><br></pre></td></tr></table></figure></p>
<p>观察者模式：<br><figure class="highlight scss"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Spring的事件驱动模型使用的是观察者模式(监听器),Spring中Observer模式常用的地方是listener的实现。</span><br></pre></td></tr></table></figure></p>
<p>策略模式：<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Spring框架的资源访问Resource接口。该接口提供了更强的资源访问能力，Spring框架本身大量使用了Resource接口来访问底层资源</span><br></pre></td></tr></table></figure></p>
<h2 id="Spring-依赖注入中的循环依赖问题"><a href="#Spring-依赖注入中的循环依赖问题" class="headerlink" title="Spring 依赖注入中的循环依赖问题"></a><strong>Spring 依赖注入中的循环依赖问题</strong></h2><h3 id="三种循环依赖的情况"><a href="#三种循环依赖的情况" class="headerlink" title="三种循环依赖的情况"></a><strong>三种循环依赖的情况</strong></h3><ul>
<li><p>构造器的循环依赖：这种依赖spring是处理不了的，直接抛出BeanCurrentlylnCreationException异常。</p>
</li>
<li><p>单例模式下的setter循环依赖：通过“三级缓存”处理循环依赖，能处理。</p>
</li>
<li><p>非单例循环依赖：无法处理。原型(Prototype)的场景是不支持循环依赖的，通常会走到<code>AbstractBeanFactory</code>类中下面的判断，抛出异常。</p>
</li>
</ul>
<figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Bean</span></span><br><span class="line">public class A &#123;</span><br><span class="line">    <span class="variable">@Autowire</span></span><br><span class="line">    private B b;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="variable">@Bean</span></span><br><span class="line">public class B &#123;</span><br><span class="line">    <span class="variable">@Autowire</span></span><br><span class="line">    private A a;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>也就是需要在A中注入B，在B中注入A，那么Spring在创建A的时候会出现这种现象：创建A实例后，在依赖注入时需要B，然后就去创建B，这时候发现又需要依赖注入 A ，这样就导致了循环依赖。</p>
<h3 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a><strong>解决方法</strong></h3><p>用三级缓存方式达到Bean提前曝光的目的。</p>
<p>Spring创建的过程简单的可以简单概括为 实例化——&gt;依赖注入——&gt;初始化。而Spring解决循环依赖的方法就是在实例化之后，依赖注入之前，将实例化的对象放到缓存中进行提前曝光，后边的对象则在实例化前，先到缓存中查找有无对应的实例化对象即可</p>
<img src="/wang-cheng/2022/03/20/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9B%9B/%E4%B8%89%E7%BA%A7%E7%BC%93%E5%AD%98.png" class title="三级缓存">
<p>或者</p>
<img src="/wang-cheng/2022/03/20/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9B%9B/Bean%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B.png" class title="Bean执行流程">
<p><strong>每一级缓存的作用：</strong><br><figure class="highlight mipsasm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">singletonObjects （一级缓存）它是我们最熟悉的朋友，俗称“单例池”“容器”，缓存创建完成单例<span class="keyword">Bean的地方。</span></span><br><span class="line"><span class="keyword"></span></span><br><span class="line">earlySingletonObjects（二级缓存）映射<span class="keyword">Bean的早期引用，也就是说在这个Map里的Bean不是完整的，甚至还不能称之为“Bean”，只是一个Instance.</span></span><br><span class="line"><span class="keyword"></span></span><br><span class="line">singletonFactories（三级缓存） 映射创建<span class="keyword">Bean的原始工厂</span></span><br></pre></td></tr></table></figure></p>
<img src="/wang-cheng/2022/03/20/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9B%9B/%E4%B8%89%E7%BA%A7%E7%BC%93%E5%AD%98%E8%A7%A3%E9%87%8A.png" class title="三级缓存解释">
<figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">实例化 <span class="selector-tag">A</span>，此时 <span class="selector-tag">A</span> 还未完成属性填充和初始化方法（@<span class="selector-tag">PostConstruct</span>）的执行，<span class="selector-tag">A</span> 只是一个半成品。</span><br><span class="line"></span><br><span class="line">为 <span class="selector-tag">A</span> 创建一个 <span class="selector-tag">Bean</span>工厂，并放入到 <span class="selector-tag">singletonFactories</span> 中。</span><br><span class="line"></span><br><span class="line">发现 <span class="selector-tag">A</span> 需要注入 <span class="selector-tag">B</span> 对象，但是一级、二级、三级缓存均为发现对象 <span class="selector-tag">B</span>。</span><br><span class="line"></span><br><span class="line">实例化 <span class="selector-tag">B</span>，此时 <span class="selector-tag">B</span> 还未完成属性填充和初始化方法（@<span class="selector-tag">PostConstruct</span>）的执行，<span class="selector-tag">B</span> 只是一个半成品。</span><br><span class="line"></span><br><span class="line">为 <span class="selector-tag">B</span> 创建一个 <span class="selector-tag">Bean</span>工厂，并放入到 <span class="selector-tag">singletonFactories</span> 中。</span><br><span class="line"></span><br><span class="line">发现 <span class="selector-tag">B</span> 需要注入 <span class="selector-tag">A</span> 对象，此时在一级、二级未发现对象</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">A</span>，但是在三级缓存中发现了对象 <span class="selector-tag">A</span>，从三级缓存中得到对象 <span class="selector-tag">A</span>，并将对象 <span class="selector-tag">A</span> 放入二级缓存中，同时删除三级缓存中的对象 <span class="selector-tag">A</span>。（注意，此时的 <span class="selector-tag">A</span></span><br><span class="line"></span><br><span class="line">还是一个半成品，并没有完成属性填充和执行初始化方法）</span><br><span class="line"></span><br><span class="line">将对象 <span class="selector-tag">A</span> 注入到对象 <span class="selector-tag">B</span> 中。</span><br><span class="line"></span><br><span class="line">对象 <span class="selector-tag">B</span> 完成属性填充，执行初始化方法，并放入到一级缓存中，同时删除二级缓存中的对象 <span class="selector-tag">B</span>。（此时对象 <span class="selector-tag">B</span> 已经是一个成品）</span><br><span class="line"></span><br><span class="line">对象 <span class="selector-tag">A</span> 得到对象<span class="selector-tag">B</span>，将对象 <span class="selector-tag">B</span> 注入到对象 <span class="selector-tag">A</span> 中。（对象 <span class="selector-tag">A</span> 得到的是一个完整的对象 <span class="selector-tag">B</span>）</span><br><span class="line"></span><br><span class="line">对象 <span class="selector-tag">A</span>完成属性填充，执行初始化方法，并放入到一级缓存中，同时删除二级缓存中的对象 <span class="selector-tag">A</span>。</span><br></pre></td></tr></table></figure>
<p>Spring 在实例化对象的之后，就会为其创建一个 Bean 工厂，并将此工厂加入到三级缓存中。</p>
<p>因此，<code>Spring 一开始提前暴露的并不是实例化的 Bean，而是将 Bean 包装起来的 ObjectFactory</code>。为什么要这么做呢？</p>
<p>这实际上涉及到 AOP，<code>如果创建的 Bean 是有代理的，那么注入的就应该是代理 Bean，而不是原始的 Bean</code>。但是 Spring 一开始并不知道 Bean 是否会有循环依赖，通常情况下（没有循环依赖的情况下），Spring 都会在完成填充属性，并且执行完初始化方法之后再为其创建代理。但是，<code>如果出现了循环依赖的话，Spring 就不得不为其提前创建代理对象，否则注入的就是一个原始对象，而不是代理对象</code></p>
<p><code>Spring 的做法就是在 ObjectFactory 中去提前创建代理对象。它会执行 getObject() 方法来获取到 Bean。</code></p>
<p>因为提前进行了代理，避免对后面重复创建代理对象，会在 earlyProxyReferences 中记录已被代理的对象。</p>
<p><strong>解决循环依赖必须要三级缓存吗</strong></p>
<p>如果没有 AOP 代理，二级缓存可以解决问题，但是有 AOP 代理的情况下，只用二级缓存就意味着所有 Bean 在实例化后就要完成 AOP 代理，这样违背了 Spring 设计的原则，Spring 在设计之初就是通过 AnnotationAwareAspectJAutoProxyCreator 这个后置处理器来在 Bean 生命周期的最后一步来完成 AOP 代理，而不是在实例化后就立马进行 AOP 代理。</p>
<h2 id="Spring-何时进行AOP"><a href="#Spring-何时进行AOP" class="headerlink" title="Spring 何时进行AOP"></a><strong>Spring 何时进行AOP</strong></h2><p>通过上面的内容我们已经解决了循环依赖的问题，在循环依赖中，我们提前进行了Bean的AOP代理工作，但是在正常情况下，Bean的AOP又是在什么时候进行的呢？</p>
<p>Spring AOP代理对象生成时机分为两类：</p>
<p>提前和非提前生成代理对象</p>
<p>提前生成代理对象就是发生了循环依赖时，利用三级缓存，生成代理对象，将代理对象注入实例化对象中。</p>
<p>非提前生成代理对象，是在bean初始化后，调用BeanPostProcessor去完成7个BeanPostProcessor子类(其中有一个BeanPostProcessor专门用来做AOP工作)的调用，来生成AOP代理对象。</p>
<img src="/wang-cheng/2022/03/20/%E9%9D%A2%E8%AF%95%E9%A2%98-%E5%9B%9B/Bean%E7%9A%84AOP%E6%B5%81%E7%A8%8B.png" class title="Bean的AOP流程">

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/" class="post-title-link" itemprop="url">面试题(三)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-19 13:57:41" itemprop="dateCreated datePublished" datetime="2022-03-19T13:57:41+08:00">2022-03-19</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-05-11 16:43:34" itemprop="dateModified" datetime="2022-05-11T16:43:34+08:00">2022-05-11</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="GC如何判断对象可以被回收"><a href="#GC如何判断对象可以被回收" class="headerlink" title="GC如何判断对象可以被回收"></a><strong>GC如何判断对象可以被回收</strong></h2><ul>
<li><p>引用计数法:每个对象有一个引用计数属性，新增一个引用时计数加一，引用释放时计数减一，计数为0时可以回收(Java不常用)</p>
</li>
<li><p>可达性分析法:从GC Roots开始向下搜索，搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时，则证明此对象是不可用的，那么虚拟机就判断是可回收对象</p>
</li>
</ul>
<figure class="highlight dns"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">引用计数法，可能会出现<span class="keyword">A</span>引用了B，B又引用了<span class="keyword">A</span>，这时候就算他们都不再使用了，但因为相互引用，计数器=<span class="number">1</span> 永远无法被回收。</span><br></pre></td></tr></table></figure>
<p>GC Roots的对象有：</p>
<ul>
<li><p>虚拟机栈(栈帧中的本地变量表)中引用的对象</p>
</li>
<li><p>方法区中类静态属性引用的对象</p>
</li>
<li><p>方法区中常量引用的对象</p>
</li>
<li><p>本地方法栈中JNI(即一般说的Native方法)引用的对象</p>
</li>
</ul>
<p>可达性算法中的不可达对象不是立即死亡的，对象拥有一次自我拯救的机会。对象被系统宣告死亡至少要经历两次标记过程:第一次是经过可达性分析发现没有与GC Roots相连接的引用链，第二次是在由虚拟机自动建立的Finalizer队列中判断是否需要执行finalizer()方法。</p>
<p>当对象变成(GC Roots)不可达时，GC会判断该对象是否覆盖了finalize方法，若未覆盖，则直接将其回收。否则，若对象未执行过finalize方法，将其放入F-Queue队列，由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后，GC会再次判断该对象是否可达，若不可达，则进行回收，否则，对象“复活”。</p>
<p>每个对象只能触发一次finalize()方法</p>
<p>由于finalize()方法运行代码高昂，不确定性大，无法保证各个对象的调用顺序，不推荐大家使用，建议遗忘它。</p>
<h2 id="对象存活判定算法"><a href="#对象存活判定算法" class="headerlink" title="对象存活判定算法"></a><strong>对象存活判定算法</strong></h2><h3 id="引用计数法"><a href="#引用计数法" class="headerlink" title="引用计数法"></a><strong>引用计数法</strong></h3><ul>
<li><p>每个对象都包含一个<code>引用计数器</code>，用于存放引用计数(实际是存放被引用的次数)</p>
</li>
<li><p>每当有一个地方引用此对象时，引用计数+1</p>
</li>
<li><p>当引用失效(比如离开了局部变量的作用域或引用被设定为null)时，引用计数-1</p>
</li>
<li><p>当引用计数为0时，表示此对象不可能再被使用，因为这时我们已经没有任何方法可以得到此对象的引用了。</p>
</li>
</ul>
<h3 id="可达性分析"><a href="#可达性分析" class="headerlink" title="可达性分析"></a><strong>可达性分析</strong></h3><p>目前比较主流的编程语言(包括Java)，一般都会使用可达性分析算法来判断对象是否存活，它采用了类似于树结构的搜索机制。</p>
<p>首先每个对象引用都有机会称为树的根节点，可以被选定作为跟节点的条件如下：</p>
<ul>
<li><p>位于虚拟机栈的栈帧中的本地变量表中所引用到的对象(其实就是我们方法中的局部变量)同样也包括本地方法栈中JNI引用的对象</p>
</li>
<li><p>类的静态成员变量引用的对象</p>
</li>
<li><p>方法区中，常量池里面引用的对象，比如我们之前提到的String类型对象。</p>
</li>
<li><p>被添加了锁的对象(比如synchronized关键字)</p>
</li>
<li><p>虚拟机内部需要用到的对象</p>
</li>
</ul>
<h3 id="最终判定"><a href="#最终判定" class="headerlink" title="最终判定"></a><strong>最终判定</strong></h3><p>虽然在经历了可达性分析算法之后基本可以判定哪些对象能够被回收，但是并不代表此对象一定会被回收，我们依然可以在最终判定阶段对其进行挽留</p>
<p>在顶级父类Object中有一个finalize()方法，该方法就是最终判定。</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/%E6%B7%98%E6%B1%B0%E6%B5%81%E7%A8%8B.png" class title="淘汰流程">
<h2 id="垃圾回收算法"><a href="#垃圾回收算法" class="headerlink" title="垃圾回收算法"></a><strong>垃圾回收算法</strong></h2><h3 id="分代收集机制"><a href="#分代收集机制" class="headerlink" title="分代收集机制"></a><strong>分代收集机制</strong></h3><p>如果我们对堆中的每一个对象都依次判定是否需要回收，这样的效率其实是很低的，那么有没有更好的回收机制呢？第一步，我们可以对堆中的对象进行分代管理</p>
<p>比如某些对象，在多次垃圾回收时，都未被判定为可回收对象，我们完全可以将这一部分对象放在一起，并让垃圾收集器减少回收此区域对象的频率，这样就能更好的提高垃圾回收效率了</p>
<p>因此，Java虚拟机将堆内存划分为<code>新生代</code>、<code>老年代</code>和<code>永久代</code>(<code>永久代</code>其实就是方法区)，这里我们主要讨论<code>新生代</code>和<code>老年代</code></p>
<p>不同的分代内存回收机制也存在一些不同之处，在HotSpot虚拟机中，新生代被划分为三块，一块较大的Eden空间和两块较小的Survivor空间，默认比例为8：1：1，老年代的GC频率相对较低，永久代一般存放类信息等(其实就是方法区的实现)，如图所示：</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/Java%E5%A0%86%E5%86%85%E5%AD%98.png" class title="Java堆内存">
<p>首先，所有新创建的对象，都会进入新生代的Eden区(如果是大对象，会直接丢尽老年区),在进行新生代区域的垃圾回收时，首先会对新生代区域的对象扫描，并回收不再使用的对象:</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6.png" class title="垃圾回收">
<p>接着，在依次垃圾回收之后，Eden区域没有被回收的对象，会进入Survivor区。在一开始From和To都是空的，而GC后，所有Eden区域存活的对象都会直接被放入到From区，最后From区和To发生一次交换，也就是说目前存放我们对象的From区，变为To区，而To区变为From区。</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B61.png" class title="垃圾回收">
<p>接着就是下一次垃圾回收了，操作与上面一样，不过这时由于我们From区域已经存在对象了，所以在Eden区的存活对象复制到From区之后，所有To区域中的对象会进行年龄判定(每经历一轮GC年龄+1，如果对象的年龄大于默认值15，那么会直接进入到老年代，否则移动到From区)</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B62.png" class title="垃圾回收">
<p>不断重复上面步骤</p>
<p>垃圾收集分为三个等级：</p>
<ol>
<li><p>Minor GC ：次要垃圾回收，主要进行新生代区域的垃圾收集</p>
<ul>
<li>触发条件：新生代的Eden区容量已满时</li>
</ul>
</li>
<li><p>Major GC :主要垃圾回收，主要进行老年代的垃圾收集</p>
</li>
<li><p>Full GC :完全垃圾回收，对整个Java堆内存和方法区进行垃圾回收</p>
<ul>
<li><p>触发条件1：每次晋升到老年代的对象平均大小大于老年代剩余空间</p>
</li>
<li><p>触发条件2：Minor GC后存活的对象超过了老年代剩余空间</p>
</li>
<li><p>触发条件3：永久代内存不足(JDK8之前)</p>
</li>
<li><p>触发条件4：手动调用System.gc()方法</p>
</li>
</ul>
</li>
</ol>
<h3 id="标记-清除算法"><a href="#标记-清除算法" class="headerlink" title="标记-清除算法"></a><strong>标记-清除算法</strong></h3><p>首先标记出所有需要回收的对象，然后再依次回收掉被标记的对象，或是标记出所有不需要回收的对象，只回收未标记的对象。实际上这种算法是非常基础的，并且最易于理解</p>
<p>虽然此方法非常简单，但缺点也非常明显，首先如果内存中存在大量的对象，那么可能就会存在大量的标记，并且大规模的进行清除。并且一次清除之后，连续的内存空间可能会出现许许多多的空隙，碎片化会导致连续内存空间利用率降低。</p>
<h3 id="标记-复制算法"><a href="#标记-复制算法" class="headerlink" title="标记-复制算法"></a><strong>标记-复制算法</strong></h3><p>既然标记清除算法在面对大量对象时效率很低，那么我们可以采用标记-复制算法。它将容量分为同样大小的两块区域</p>
<p>标记复制算法，实际上就是将内存区域划分为相同大小的两块区域，每次只使用其中的一块区域，每次垃圾回收结束后，将所有存活的对象全部复制到另一块区域中，并一次性清空当前区域。虽然浪费了一些时间进行复制操作，但是这样能够很好的解决对象大面积回收后空间碎片化严重的问题</p>
<p>这种算法非常适用于新生代(因为新生代的回收率极高，一般不会留下太多的对象)的垃圾回收，而我们之前所说的新生代Survivor区其实就是这个思路，包括8:1:1的比例也是为了对标记复制算法进行优化而采取的。</p>
<h3 id="标记-整理算法"><a href="#标记-整理算法" class="headerlink" title="标记-整理算法"></a><strong>标记-整理算法</strong></h3><p>虽然标记-复制算法能够很好的应对新生代高回收率的场景，但是放到老年代，它就显得很鸡肋了。我们知道，一般长期都回收不到的对象，才有机会进入到老年代，所以老年代一般都是些钉子户，可能一次GC后，仍然存留很多对象。而标记复制算法会在GC后完整复制整个区域内容，并且会折损50%的区域，这样显然不适用于老年代</p>
<p>那么我们能否这样，在标记所有待回收对象之后，不急着去进行回收操作，而是将所有待回收的对象整齐排列在一段内存空间中，而需要回收的对象全部往后丢，这样，前半部分的所有对象都是无需进行回收的，而后半部分直接一次性清除即可。</p>
<p>虽然这样能保证内存空间充分使用，并且也没有标记复制算法那么繁杂，但是缺点也是显而易见的，它的效率比前两者都低。甚至由于需要修改对象在内存中的位置，此时程序必须要暂停才可以，在极端情况下，可能会导致整个程序发生停顿</p>
<p>所以，我们可以将标记清除算法和标记整理算法混合使用，在内存空间还不是很凌乱的时候，采用标记清除算法其实是没有多大问题的，当内存空间凌乱到一定程度后，我们可以进行一次标记整理算法。</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/Java%E5%A0%86%E5%86%85%E5%AD%98.png" class title="Java堆内存">
<h2 id="常用垃圾回收器有哪些"><a href="#常用垃圾回收器有哪些" class="headerlink" title="常用垃圾回收器有哪些"></a><strong>常用垃圾回收器有哪些</strong></h2><h3 id="Serial收集器"><a href="#Serial收集器" class="headerlink" title="Serial收集器"></a><strong>Serial收集器</strong></h3><p>元老级收集器，在JDK1.3之前，是虚拟机新生代区域收集器的唯一选择。这时一款单线程的垃圾收集器，也就是说，当开始进行垃圾回收时，需要暂停所有线程，直到垃圾收集工作结束。它的新生代收集算法采用的是标记复制算法，老年代采用的是标记整理算法。</p>
<p>优点：</p>
<ol>
<li>设计简单而高效</li>
<li>在用户的桌面应用场景中，内存一般不大，可以在较短时间内完成垃圾收集，只要不频繁发生，使用串行回收器是可以接收的</li>
</ol>
<p>缺点：单线程，在进行垃圾回收时，需要暂停所有线程。</p>
<h3 id="ParNew收集器"><a href="#ParNew收集器" class="headerlink" title="ParNew收集器"></a><strong>ParNew收集器</strong></h3><p>相当于是Serial收集器的多线程版本，能够支持多线程垃圾收集</p>
<p>同样是先暂停所有的线程，然后再多线程去进行垃圾回收</p>
<h3 id="Parallel-Scavenge-Parallel-Old收集器"><a href="#Parallel-Scavenge-Parallel-Old收集器" class="headerlink" title="Parallel Scavenge/Parallel Old收集器"></a><strong>Parallel Scavenge/Parallel Old收集器</strong></h3><p>Parallel Scavenge同样是一款面向新生代的垃圾收集器，同样采用标记复制算法实现，在JDK6时也推出了其老年代收集器Parallel Old，采用标记整理算法实现</p>
<p>同样是先暂停所有的线程，然后再多线程去进行垃圾回收</p>
<p>与ParNew收集器不同的是，它会自动衡量一个吞吐量，并根据吞吐量来决定每次垃圾回收的时间，这种自适应机制，能够很好的权衡当前机器的性能，根据性能选择最优方案。</p>
<p>目前JDK8采用的就是这种Parallel Scavenge + Parallel Old的垃圾回收方案</p>
<h3 id="CMS收集器"><a href="#CMS收集器" class="headerlink" title="CMS收集器"></a><strong>CMS收集器</strong></h3><p>在JDK1.5,HotSpot推出了一款在强交互应用中几乎认为有划时代意义的垃圾收集器：CMS(Concurrent-Mark-Sweep)收集器，这款收集器是HotSpot虚拟机中第一款真正意义上的并发(注意这里的并发和之前的并行是有区别的，并发可理解为同时运行用户线程和GC线程，而并行可以理解为多条GC线程同时工作)收集器，它第一次实现了让垃圾收集线程与用户线程同时工作。</p>
<p>采用的是标记清除算法</p>
<p>它的垃圾回收分为4个阶段：</p>
<ul>
<li><p>初始标记(需要暂停用户线程)：这个阶段的主要任务仅仅只是标记出GC Roots能直接关联到的对象，速度比较快，不用担心会停顿太长时间</p>
</li>
<li><p>并发标记：从GC Roots的直接关联对象开始遍历整个对象图的过程，这个过程耗时较长但是不需要停顿用户线程，可以与垃圾收集线程一起并发运行</p>
</li>
<li><p>重新标记(需要暂停用户线程)：由于并发标记阶段可能某些用户线程会导致标记产生变动，因此这里需要再次暂停所有线程进行并发标记，这个时间会比初始标记时间长一点</p>
</li>
<li><p>并发清除：最后就可以直接将所有标记好的无用对象进行删除，因为这些对象程序中也用不到了，所以可以与用户线程并发运行。</p>
</li>
</ul>
<p>虽然它的有点非常之大，但是缺点也是显而易见的，我们之前说过，标记清除算法会产生大量的内存碎片，导致可用连续空间逐渐变少，长期这样下来，会有更高的概率触发Full GC，并且在与用户线程并发执行的情况下，也会占用一部分的系统资源，导致用户线程的运行速度一定程度上减慢</p>
<p>不过，如果你希望的是最低的GC停顿时间，这款垃圾收集器无疑是最佳选择，不过自从G1收集器问世之后，CMS收集器不再推荐使用</p>
<h3 id="Garbage-First-G1-收集器"><a href="#Garbage-First-G1-收集器" class="headerlink" title="Garbage First(G1)收集器"></a><strong>Garbage First(G1)收集器</strong></h3><p>此垃圾收集器也是一款划时代的垃圾收集器，在JDK7的时候正式走上历史舞台，它是一款主要面向于服务端的垃圾收集器，并且在JDK9时，取代了JDK8默认的Parallel Scavenge + Parallel Old的回收方案。</p>
<p>我们知道，我们的垃圾回收分为 Minor GC,Major GC 和 Full GC,他们分别对应的是新生代、老年代和整个堆内存的垃圾回收，而G1收集器巧妙地绕过了这些约定，它将整个Java堆划分为2048个大小相同的独立Region块，每个Region块的大小根据堆空间的实际大小而定，整体被控制在1MB到32MB之间，且都为2的N次幂，所有的Region大小相同，且在JVM的整个生命周期内不会发生改变。</p>
<p>那么分出这些Region有什么意义呢？每个Region都可以根据需要，自由决定扮演哪个角色(Eden、Survivor和老年代)，收集器会根据对应的角色采用不同的回收策略。此外，G1收集器还存在一个Humongous区域，它专门用于存放大对象(一般认为大小超过了Region容量的一半的对象为大对象)这样，新生代、老年代在物理上，不再是一个连续的内存区域，而是到处分布的。</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/G1%E6%94%B6%E9%9B%86%E5%99%A8%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B.png" class title="G1收集器内存模型">
<p>它的回收过程与CMS大体类似：</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/G1%E7%BA%BF%E7%A8%8B%E6%A8%A1%E5%9E%8B.png" class title="G1线程模型">
<p>分为以下四个步骤：</p>
<ul>
<li><p>初始标记(暂停用户线程)：仅仅只是标记一下GC Roots能直接关联到的对象，并且修改TAMS指针的值，让下一阶段用户线程并发运行时，能正确的在可用的Region中分配新对象。这个阶段需要停顿线程，但耗时很短，而且是借用进行Minor GC的时候同步完成的，所以G1收集器在这个阶段实际并没有额外的停顿。</p>
</li>
<li><p>并发标记：从GC Roots开始对堆中的对象进行可达性分析，递归扫描整个堆里的对象图，找出要回收的对象，这阶段耗时较长，但可与用户程序并发执行</p>
</li>
<li><p>最终标记(暂停用户线程)：对用户线程做一个短暂的暂停，用于处理并发标记阶段漏标的那部分对象</p>
</li>
<li><p>筛选回收：负责更新Region的统计数据，对各个Region的回收价值和成本进行排序，根据用户所期望的停顿时间来指定回收计划，可以自由选择任意多个Region构成回收集，然后把决定回收的那一部分Region的存活对象复制到空的Region中，再清理掉整个旧Region的全部空间。这里的操作设计存活对象的移动，是必须暂停用户线程，由多个收集器线程并行完成的。</p>
</li>
</ul>
<p><strong>G1（Garbage first ）</strong></p>
<p>垃圾收集器是目前垃圾收集器理论发展的最前沿成果，相比与 CMS 收集器，G1 收集器两个最突出的改进是：</p>
<ol>
<li>基于标记-整理算法，不产生内存碎片。</li>
<li>可以非常精确控制停顿时间，在不牺牲吞吐量前提下，实现低停顿垃圾回收。</li>
</ol>
<p>G1 收集器避免全区域垃圾收集，它把堆内存划分为大小固定的几个独立区域，并且跟踪这些区域的垃圾收集进度，同时在后台维护一个优先级列表，每次根据所允许的收集时间，优先回收垃圾最多的区域。区域划分和优先级区域回收机制，确保 G1 收集器可以在有限时间获得最高的垃圾收集效率。</p>
<h2 id="线程生命周期，线程有哪些状态"><a href="#线程生命周期，线程有哪些状态" class="headerlink" title="线程生命周期，线程有哪些状态"></a><strong>线程生命周期，线程有哪些状态</strong></h2><ol>
<li>线程通常有5中状态，创建、就绪、运行、阻塞和死亡</li>
<li><p>阻塞的情况又分为三种</p>
<ol>
<li>等待阻塞：运行的线程执行wait方法，该线程会释放占用的所有资源，JVM会把该线程放入”等待池”中。进入这个状态后，是不能自动唤醒的，必须依靠其他线程调用notify或notifyAll方法才能被唤醒，wait是object类的方法</li>
<li>同步阻塞：运行的线程在获取对象的同步锁时(竞争锁)，若该同步锁被别的线程占用(没有抢到锁)，则JVM会把线程放入”锁池”中。</li>
<li>其他阻塞：运行的线程执行sleep或join方法，或者发出了I/O请求，JVM会把该线程置为阻塞状态。当sleep状态超时、join等待线程终止或者超时、或者I/O处理完成时，线程重新转入就绪状态。sleep是Thread类中的方法，wait是object类中的方法。</li>
</ol>
</li>
<li><p>新建状态(New)：创建了一个线程对象</p>
</li>
<li><p>就绪状态(Runnable)：线程对象创建后，其他线程调用该对象的start方法。该状态的线程位于可运行线程池中，变得可运行，等待获取CPU的使用权</p>
</li>
<li><p>运行状态(Running)：就绪状态的线程获取了CPU，执行程序代码</p>
</li>
<li><p>阻塞状态(Blocked)：阻塞状态是线程因为某种原因放弃CPU使用权，暂时停止运行。直到线程进入就绪状态，才有机会转到运行状态。</p>
</li>
<li><p>死亡状态(Dead)：线程执行完了或者因异常退出了run方法，该线程结束生命周期。</p>
</li>
</ol>
<h2 id="sleep-、wait-、join-、yield-的区别"><a href="#sleep-、wait-、join-、yield-的区别" class="headerlink" title="sleep()、wait()、join()、yield()的区别"></a><strong>sleep()、wait()、join()、yield()的区别</strong></h2><p>1、锁池：所有需要竞争同步锁的线程都会放在锁池当中，比如当前对象的锁已经被其中一个线程得到，则其他线程需要在这个锁池进行等待，当前面的线程释放同步锁后锁池中的线程去竞争同步锁，当某个线程得到后会进入就绪队列进行等待CPU资源分配。</p>
<p>2、等待池(主要针对wait方法)：当我们调用(能调用方法，说明此时这个线程应该是拿到锁的一个状态)wait()后，线程会放到等待池中，等待池中的线程是不会去竞争同步锁(因为wait方法将这个锁给释放掉了，不允许他们再去竞争锁了)。只有调用了notify()或notifyAll()后等待池的线程才会开始去竞争锁，notify()是随机从等待池选出一个线程放到锁池，而notifyAll()是将等待池中的所有线程放到锁池中。</p>
<ol>
<li><p>sleep是Thread类的静态本地方法，wait则是Object类的本地方法</p>
</li>
<li><p>sleep方法不会释放lock，但是wait会释放，而且会加入到等待池。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sleep</span>就是把cpu的执行资格和执行权释放出去，不再运行此线程，当定时时间结束再取回cpu资源，参与cpu调度，获取到cpu资源后就可以继续运行了。而如果调<span class="built_in">sleep</span>时该线程有锁(调用<span class="built_in">sleep</span>的代码块有synchronized修饰)，那么<span class="built_in">sleep</span>不会释放这个锁，而是把锁带着进入冻结状态，也就是说其他需要这个锁的线程不可能拿到这个锁，也就是说这个程序无法执行。如果在睡眠期间其他线程调用了这个线程的interrupt方法，那么这么线程会抛出interrupteexception异常返回，这点和<span class="built_in">wait</span>是一样的。</span><br></pre></td></tr></table></figure>
</li>
<li><p>sleep方法不依赖于同步器synchronized(没有被synchronized修饰的代码块中也可以使用sleep)，但是wait需要依赖synchronized关键字(两者必须配套使用)。</p>
</li>
<li><p>sleep不需要被唤醒(休眠之后退出阻塞)，但是wait需要(不指定时间需要被别人中断，如果带上时间参数，也是不需要被唤醒的)</p>
</li>
<li><p>sleep一般用于当前线程休眠，或者轮循暂停操作，wait则多用于多线程之间的通信。</p>
</li>
<li><p>sleep会让出cpu时间，且强制上下文切换，而wait则不一定会让出cpu，wait后可能还是有机会重新竞争带锁继续执行的(wait后如果重新竞争到了锁，那么是不需要进行上下文切换的)</p>
</li>
</ol>
<p>yield()执行后线程直接进入就绪状态(就绪状态而不是阻塞状态，这说明该线程可以继续竞争锁)，马上释放了cpu的执行权，但是依然保留了cpu的执行资格，所以有可能cpu下次进行线程调用还会让这个线程获取到执行权继续执行(也就是说yield啥事也没干，继续往下执行)</p>
<p>join()执行后线程进入阻塞状态，例如在线程B中调用线程A的join()，那么线程B会进入阻塞状态，直到线程A结束或中断线程(注意是中断线程，如果阻塞的话是需要继续等待阻塞完成的)<br><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>) throws InterruptedException</span>&#123;</span><br><span class="line">    Thread t1 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable()&#123;</span><br><span class="line">        @Override</span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span>&#123;</span><br><span class="line">            <span class="keyword">try</span>&#123;</span><br><span class="line">                Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)&#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            System.<span class="keyword">out</span>.println(<span class="string">&quot;t1线程&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    t1.start();</span><br><span class="line">    t1.<span class="keyword">join</span>();</span><br><span class="line">    <span class="comment">//这行代码必须要等t1全部执行完毕，才会执行</span></span><br><span class="line">    System.<span class="keyword">out</span>.println(<span class="string">&quot;main线程&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line">ti线程</span><br><span class="line">main线程</span><br></pre></td></tr></table></figure></p>
<h2 id="对线程安全的理解"><a href="#对线程安全的理解" class="headerlink" title="对线程安全的理解"></a><strong>对线程安全的理解</strong></h2><p>讲线程安全，实际上指的不是线程安全，应该是内存安全，堆是共享内存，可以被所有线程访问<br><figure class="highlight gcode"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">当多个线程访问同一个对象<span class="comment">(对象是处在堆中的)</span>，如果不用进行额外的同步控制或其他的协调操作，调用这个对象的行为都可以获得正确的结果，我们就说这个对象是线程安全的。</span><br></pre></td></tr></table></figure></p>
<p><strong>堆</strong>是进程和线程(所有线程共享同一个堆)共有的空间，分全局堆和局部堆。全局堆就是所有没有分配的空间，局部堆就是用户分配的空间(已经被用户分配了的空间)。堆在操作系统对进程初始化的时候分配，运行过程中也可以向系统要额外的堆，但是用完了要还给操作系统，要不然就是内存泄漏。<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">在java中，堆是java虚拟机所管理的内存中最大的一块，是所有线程共享的一块内存区域，在虚拟机启动的时候创建。堆所存在的内存区域的唯一目的就是存放对象实例，几乎所有的对象实例以及数组都在这里分配内存。</span><br></pre></td></tr></table></figure></p>
<p><strong>栈</strong>是每个线程独有的，保存其运行状态和局部变量。栈在线程开始的时候初始化，每个线程的栈互相独立，因此，栈是线程安全的。操作系统在切换线程的时候会自动切换栈。栈空间不需要在高级语言中显示得分配和释放。</p>
<p>目前主流操作系统都是多任务的，即多个进程同时运行。为了保证安全，每个进程只能访问分配给自己的内存空间，而不能访问别的进程的，这是由操作系统保障的。</p>
<p>在每个进程的内存空间中都会有一块特殊的公共区域，通常称为堆(内存)。进程内的所有线程都可以访问到该区域，这就是造成问题的潜在原因。</p>
<h2 id="Thread、Runnable的区别"><a href="#Thread、Runnable的区别" class="headerlink" title="Thread、Runnable的区别"></a><strong>Thread、Runnable的区别</strong></h2><p>Thread和Runnable的实质是继承关系，没有可比性。Thread实现了Runnable接口。无论使用Runnable还是Thread，都会new Thread，然后执行run方法。用法上，如果有复杂的线程操作需求，那就选择继承Thread，如果只是简单的执行一个任务，那就实现Runnable。</p>
<figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//会卖出多一倍的票</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> void main(<span class="keyword">String</span>[] args)&#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> <span class="type">MyThread</span>().start;</span><br><span class="line">        <span class="keyword">new</span> <span class="type">MyThread</span>().start;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyThread</span> <span class="keyword"><span class="keyword">extends</span> <span class="type">Thread</span></span></span>&#123;</span><br><span class="line">        <span class="keyword">private</span> int ticket = <span class="number">5</span>;</span><br><span class="line">        <span class="keyword">public</span> void run()&#123;</span><br><span class="line">            <span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;Thread ticket = &quot;</span> + ticket--);</span><br><span class="line">                <span class="keyword">if</span>(ticket &lt; <span class="number">0</span>)&#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//正常卖出</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test2</span></span>&#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> void main(<span class="keyword">String</span>[] args)&#123;</span><br><span class="line"></span><br><span class="line">        MyThread2 mt = <span class="keyword">new</span> <span class="type">MyThread2</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="type">Thread</span>(mt).start;</span><br><span class="line">        <span class="keyword">new</span> <span class="type">Thread</span>(mt).start;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">MyThread2</span> <span class="keyword"><span class="keyword">implements</span> <span class="type">Runnable</span></span></span>&#123;</span><br><span class="line">        <span class="keyword">private</span> int ticket = <span class="number">5</span>;</span><br><span class="line">        <span class="keyword">public</span> void run&#123;</span><br><span class="line">            <span class="keyword">while</span>(<span class="literal">true</span>)&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;Runnable ticket = &quot;</span> + ticket--);</span><br><span class="line">                <span class="keyword">if</span>(ticket &lt; <span class="number">0</span>)&#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>原因是第一个例子new了两个实例，启动了两个线程，而MyThread的ticket是成员变量，相当于有两个ticket，所以肯定会打印两遍ticket。而第二个只new了一个实例，启动两个线程，这两个线程共享一个ticket，所以只会打印一遍。</p>
<h2 id="对守护线程的理解"><a href="#对守护线程的理解" class="headerlink" title="对守护线程的理解"></a><strong>对守护线程的理解</strong></h2><p>守护线程：为所有非守护线程提供服务的线程(Java中只有两种线程，守护线程、用户线程);任何一个守护线程都是整个JVM中所有非守护线程的保姆。</p>
<p>守护线程类似于整个进程的一个默默无闻的人;它的生死无关重要，它却依赖整个进程而运行，哪天其他线程结束了，没有要执行的了，程序就结束了，理都没理守护线程，就把它中断了。</p>
<p>注意：由于守护线程的终止是自身无法控制的，因此千万不要把IO、File等重要操作逻辑分配给它，因为它不靠谱。</p>
<p>守护线程的作用：</p>
<p>举例，GC垃圾回收线程：就是一个经典的守护线程，当我们的程序中不再有任何运行的Thread，程序就不会再产生垃圾，垃圾回收器也就无事可做，所以当垃圾回收线程是JVM上仅剩的线程时，垃圾回收线程会自动离开。它始终在低级别的状态中运行，用于实时监控和管理系统中的可回收资源。</p>
<p>应用场景：(1) 来为其他线程提供一些服务支持 (2) 或者在任何情况下，程序结束时，这个线程必须正常且立刻关闭，就可以作为守护线程来使用，反之，如果一个正在执行某个操作的线程必须要正确地关闭掉否则就会出现不好的后果，那么这个线程就不能是守护线程，而是用户线程。通常都是些关键的事务，比如说，数据库的录入或者更新，这些操作都是不可中断的。</p>
<p>使用方法：</p>
<p>thread.setDaemon(true)必须在thread.start()之前设置，否则会报错IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。</p>
<p>在Daemon线程中产生的新线程也是Daemon的。</p>
<p>守护线程不能去访问固有资源，比如读写操作或者计算逻辑。因为它会在任何时候甚至在一个操作的中间发生中断。</p>
<p>Java自带的多线程框架，比如ExecutorService，会将守护线程转换为用户线程(也就是说所有线程都会成为用户线程)，所以如果要使用后台线程就不能用Java的线程池。</p>
<h2 id="ThreadLocal的原理和使用场景"><a href="#ThreadLocal的原理和使用场景" class="headerlink" title="ThreadLocal的原理和使用场景"></a><strong>ThreadLocal的原理和使用场景</strong></h2><p>每一个Thread对象均包含有一个ThreadLocalMap(key是ThreadLocal，value是要存的值)类型的成员变量threadLocals，它存储本线程中所有ThreadLocal对象以及对应的值。</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/ThreadLocalMap.png" class title="ThreadLocalMap">
<p>ThreadLocalMap由一个个Entry对象构成</p>
<p>Entry继承自WeakReference<ThreadLocal<?>&gt;，一个Entry由ThreadLocal对象和Object构成。由此可见，Entry的key是ThreadLocal对象，并且是一个弱引用。当没有指向key的强引用之后，该key就会被垃圾回收器回收</ThreadLocal<?></p>
<p>(ThreadLocal的使用)当执行set方法后，ThreadLocal首先会获取当前线程对象(Thread)，然后获取当前线程中ThreadLocalMap对象。再以当前ThreadLocal对象为key，将值存储进ThreadLocalMap(它是属于当前线程的)对象中。</p>
<p>get方法类似。ThreadLocal首先会获取当前线程对象，然后获取当前线程的ThreadLocalMap对象。再以当前ThreadLocal对象为key，获取对应的value。</p>
<p>由于每一条线程都含有一个<strong>私有</strong>的ThreadLocalMap，这些容器相互独立互不影响，因此不会存在线程安全问题，从而也无需使用同步机制来保证多条线程访问容器的互斥性。</p>
<p>使用场景：</p>
<ol>
<li><p>在进行对象跨层传递的时候，使用ThreadLocal可以避免多次传递，打破层次间的约束(例如controller层与service层、dao层之间传递多个参数，如果不使用ThreadLocal的话，可能每一层都需要在方法上定义参数去传递，而使用ThreadLocal后，因为ThreadLocal是属于这个线程的，无论程序运行到哪里都是这一个线程，只需要在Controller中存储这个参数，然后可以在程序执行的任何地方取出这个参数进行使用(个人理解))</p>
</li>
<li><p>线程间数据隔离(ThreadLocal是属于线程私有，只有当前线程能够访问到，其他线程访问不到，起到数据隔离效果)</p>
</li>
<li><p>进行事务操作，用于存储线程事务信息</p>
</li>
<li><p>数据库连接，Session会话管理</p>
</li>
</ol>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Spring框架在事务开始时会给当前线程绑定一个Jdbc <span class="keyword">Connection</span>，在整个事务过程都是使用该线程绑定的<span class="keyword">Connection</span>来执行数据库操作，实现了事务的隔离性。Spring框架里面就是用的ThreadLocal来实现这种隔离</span><br></pre></td></tr></table></figure>
<h2 id="ThreadLocal内存泄漏的原因，如何避免"><a href="#ThreadLocal内存泄漏的原因，如何避免" class="headerlink" title="ThreadLocal内存泄漏的原因，如何避免"></a><strong>ThreadLocal内存泄漏的原因，如何避免</strong></h2><p>内存泄漏为程序在申请内存后，无法释放已申请的内存空间，一次内存泄漏危害可以忽略，但内存泄漏堆积后果很严重，无论多少内存，迟早会被占光。</p>
<p>不再会被使用的对象或变量占用的内存不能被回收，就是内存泄漏(内存不够是OOM，多次的内存泄漏，最终会导致OOM)</p>
<p>强引用:使用最普通的引用(New)(反射)，一个对象具有强引用，不会被垃圾回收器回收。当内存空间不足，Java虚拟机宁愿抛出OutOfMemoryError错误，使程序异常终止，也不回收这种对象。</p>
<p>如果想取消强引用和某个对象的关联，可以显式地将引用赋值为null，这样可以使JVM在合适地时间就会回收该对象。</p>
<p>弱引用:JVM进行垃圾回收时，无论内存是否充足，都会回收弱引用关联的对象。在Java中，用java.lang.ref.WeakReference类来表示。可以在缓存中使用弱引用(缓存中使用弱引用，被回收也没关系)</p>
<p>ThreadLocal的实现原理，每个Thread维护一个ThreadLocalMap，key为使用弱引用的ThreadLocal实例，value为线程变量的副本</p>
<img src="/wang-cheng/2022/03/19/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%89/%E5%BC%B1%E5%BC%95%E7%94%A8.png" class title="弱引用">
<p>(实线是强引用，虚线是弱引用)</p>
<p>ThreadLocalMap使用ThreadLocal的弱引用作为key，如果一个ThreadLocal不存在外部强引用时，Key(ThreadLocal)势必会被GC回收，这样就会导致ThreadLocalMap中key为null，而value还存在强引用，只有Thread线程退出之后，value的强引用链才会断，但如果当前线程再迟迟不结束的话，这些key为null的Entry的value就会一直存在一条强引用链(红色链条)(这个value就一直回收不掉，就会产生内存泄漏)</p>
<p>key使用强引用</p>
<p>当ThreadLocalMap的key为强引用回收ThreadLocal时，因为ThreadLocalMap还持有ThreadLocal的强引用，如果没有手动删除，ThreadLocal不会被回收，导致Entry内存泄漏</p>
<p>key使用弱引用</p>
<p>当ThreadLocalMap的key为弱引用回收ThreadLocal时，由于ThreadLocalMap持有ThreadLocal的弱引用，即使没有手动删除，ThreadLocal也会被回收。当key为null时，在下一次ThreadLocalMap调用set(),get(),remove()方法的时候会被清除value值。</p>
<p>因此，ThreadLocalMap内存泄漏的根源是：由于ThreadLocalMap的生命周期跟Thread一样长，如果没有手动删除对应key就会导致内存泄漏，而不是因为弱引用</p>
<p>ThreadLocal正确的使用方法：</p>
<ul>
<li><p>每次使用完ThreadLocal都调用它的remove()方法清楚数据</p>
</li>
<li><p>将ThreadLocal变量定义成private static，这样就一直存在ThreadLocal的强引用，也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值，进而清除掉。</p>
</li>
</ul>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/18/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%BA%8C/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/18/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%BA%8C/" class="post-title-link" itemprop="url">面试题(二)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-18 11:18:51" itemprop="dateCreated datePublished" datetime="2022-03-18T11:18:51+08:00">2022-03-18</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-04-30 21:30:58" itemprop="dateModified" datetime="2022-04-30T21:30:58+08:00">2022-04-30</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h2 id="hashCode与equals"><a href="#hashCode与equals" class="headerlink" title="hashCode与equals"></a><strong>hashCode与equals</strong></h2><h3 id="hashCode介绍"><a href="#hashCode介绍" class="headerlink" title="hashCode介绍"></a><strong>hashCode介绍</strong></h3><p>hashCode()的作用是获取哈希码，也称为散列码；实际上返回的是一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()定义在JDK的Objcet.java中，Java中的任何类都包含有hashCode()函数。散列表中存的是键值对(key-value)，它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码！(可以快速找到所需要的对象)</p>
<h3 id="为什么要有hashCode"><a href="#为什么要有hashCode" class="headerlink" title="为什么要有hashCode()"></a><strong>为什么要有hashCode()</strong></h3><p><strong>以“HashSet如何检查重复”为例子来说明什么要有hashCode</strong></p>
<p>对象加入HashSet时，HashSet会先计算对象的hashcode值来判断对象加入的位置，看该位置是否有值，如果没有、HashSet会假设对象没有重复出现。但是如果发现有值，这时会调用equals()方法来检查两个对象是否真的相同。如果两者相同，HashSet就不会让其加入操作成功。如果不同得话，就会重新散列(重新调用散列算法计算出一个散列值，这个散列值肯定会做一些操作比如加盐，否则计算出来的散列值与第一次计算出来的散列值相同，就是做无用功了)到其他位置。这样就大大减少了equals的次数，相应就大大提高了执行速度。</p>
<ul>
<li>如果两个对象相等，则hashcode一定也是相同的</li>
<li>两个对象相等，对两个对象分别调用equals方法都返回true</li>
<li>两个对象有相同的hashcode值，它们也不一定是相等的(所以才会有HashSet在加入对象时，如果发现有值，则会调用equals方法再来判断是否真的相等)</li>
<li>因此，equals方法被覆盖过，则hashcode方法也必须被覆盖</li>
<li>hashcode()的默认行为是对堆上的对象产生独特值。如果没有重写hashcode()，则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)</li>
</ul>
<h2 id="为什么要使用HashMap"><a href="#为什么要使用HashMap" class="headerlink" title="为什么要使用HashMap"></a><strong>为什么要使用HashMap</strong></h2><p>对于要求查询次数特别多，查询效率比较高同时插入和删除的次数比较少的情况下，通常会选择ArrayList，因为它的底层是通过数组实现的。对于插入和删除次数比较多同时在查询次数不多的情况下，通常会选择LinkedList，因为它的底层是通过链表实现的。</p>
<p>但现在同时要求插入，删除，查询效率都很高的情况下我们该如何选择容器呢？<br>那么就有一种新的容器叫HashMap，他里面既有数组结构，也有链表结构，所以可以弥补相互的缺点。而且HashMap主要用法是get()和put() 。</p>
<h2 id="HashMap如何扩容"><a href="#HashMap如何扩容" class="headerlink" title="HashMap如何扩容"></a><strong>HashMap如何扩容</strong></h2><p>HashMap的size大于等于(容量*加载因子)的时候,会触发扩容的操作,这个是个代价不小的操作。</p>
<p>为什么要扩容呢？HashMap默认的容量是16,随着元素不断添加到HashMap里,出现hash冲突的机率就更高,那每个桶对应的链表就会更长,</p>
<p>这样会影响查询的性能,因为每次都需要遍历链表,比较对象是否相等,一直到找到元素为止。</p>
<p>为了提升查询性能,只能扩容,减少hash冲突,让元素的key尽量均匀的分布。</p>
<p>扩容的时候需要重新计算元素的数组下标</p>
<p>1、重新分配一个新的Entry数组</p>
<p>2、重新计算原来元素的在新数组中的下标(比较耗资源)</p>
<p>index = HashCode（key） &amp; (Length - 1) jdk1.8 </p>
<p>扩容时使用位运算，<code>(n-1) &amp; hash</code> 。</p>
<p>因为用乘法会影响CPU的性能，计算机不支持乘法运算，最终都会转化为加法运算。</p>
<h2 id="JDk1-7HashMap扩容死循环问题"><a href="#JDk1-7HashMap扩容死循环问题" class="headerlink" title="JDk1.7HashMap扩容死循环问题"></a><strong>JDk1.7HashMap扩容死循环问题</strong></h2><p>HashMap是一个线程不安全的容器，在最坏的情况下，所有元素都定位到同一个位置，形成一个长长的链表，这样get一个值时，最坏情况需要遍历所有节点，性能变成了O(n)。</p>
<p>JDK1.7中HashMap采用头插法拉链表，所谓头插法，即在每次都在链表头部（即桶中）插入最后添加的数据。</p>
<p>死循环问题只会出现在多线程的情况下。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">假设在原来的链表中，<span class="selector-tag">A</span>节点指向了<span class="selector-tag">B</span>节点。</span><br><span class="line">在线程<span class="number">1</span>进行扩容时，由于使用了头插法，链表中<span class="selector-tag">B</span>节点指向了<span class="selector-tag">A</span>节点。</span><br><span class="line">在线程<span class="number">2</span>进行扩容时，由于使用了头插法，链表中<span class="selector-tag">A</span>节点又指向了<span class="selector-tag">B</span>节点。</span><br><span class="line">在线程n进行扩容时，…</span><br><span class="line">这就容易出现问题了。。在并发扩容结束后，可能导致<span class="selector-tag">A</span>节点指向了<span class="selector-tag">B</span>节点，<span class="selector-tag">B</span>节点指向了<span class="selector-tag">A</span>节点，链表中便有了环！！！</span><br></pre></td></tr></table></figure>
<h2 id="HashMap和HashTable的区别、底层实现是什么"><a href="#HashMap和HashTable的区别、底层实现是什么" class="headerlink" title="HashMap和HashTable的区别、底层实现是什么"></a><strong>HashMap和HashTable的区别、底层实现是什么</strong></h2><p>1、区别:</p>
<ul>
<li><p>HashMap方法没有synchronized修饰，线程非安全，HashTable线程安全(HashTable的添加、删除等方法都有synchronized修饰)</p>
</li>
<li><p>HashMap允许key和value为null，而HashTable不允许</p>
</li>
</ul>
<p>2、底层实现：数组+链表</p>
<p>jdk8开始链表高度到8、数组长度超过64，链表转变为红黑树，元素以内部类Node节点存在</p>
<ul>
<li><p>计算key的hash值，二次hash(再次计算hash值)然后对数组长度取模，对应到数组下标</p>
</li>
<li><p>如果没有产生hash冲突(下标位置没有元素)，则直接创建Node存入数组</p>
</li>
<li><p>如果产生hash冲突，先进行equals比较，相同则取代该元素，不同，则判断链表高度插入链表，链表高度达到8，并且数组长度到64则转变为红黑树，长度低于6则将红黑树转回链表</p>
</li>
<li><p>key为null，存到下标为0的位置</p>
</li>
</ul>
<h2 id="HashMap扩容为什么总是2的次幂？"><a href="#HashMap扩容为什么总是2的次幂？" class="headerlink" title="HashMap扩容为什么总是2的次幂？"></a><strong>HashMap扩容为什么总是2的次幂？</strong></h2><p>HashMap扩容主要是给数组扩容的，因为数组长度不可变，而链表是可变长度的。从HashMap的源码中可以看到HashMap在扩容时选择了位运算，向集合中添加元素时，会使用(n - 1) &amp; hash的计算方法来得出该元素在集合中的位置。只有当对应位置的数据都为1时，运算结果也为1，当HashMap的容量是2的n次幂时，(n-1)的2进制也就是1111111<em>*</em>111这样形式的，这样与添加元素的hash值进行位运算时，能够充分的散列，使得添加的元素均匀分布在HashMap的每个位置上，减少hash碰撞。</p>
<p>另外，HashMap的初始容量是2的n次幂，扩容也是2倍的形式进行扩容，是因为容量是2的n次幂，可以使得添加的元素均匀分布在HashMap中的数组上，减少hash碰撞，避免形成链表的结构，使得查询效率降低！</p>
<p>index = HashCode（key） &amp; (Length - 1) jdk1.8</p>
<h2 id="HashMap负载因子为什么是0-75"><a href="#HashMap负载因子为什么是0-75" class="headerlink" title="HashMap负载因子为什么是0.75"></a><strong>HashMap负载因子为什么是0.75</strong></h2><p>通俗来讲，当负载因子为1.0时，意味着只有当hashMap装满之后才会进行扩容，虽然空间利用率有大的提升，但是这就会导致大量的hash冲突，使得查询效率变低。</p>
<h2 id="为什么非要使用红黑树呢？"><a href="#为什么非要使用红黑树呢？" class="headerlink" title="为什么非要使用红黑树呢？"></a><strong>为什么非要使用红黑树呢？</strong></h2><p>这个选择是综合各种考虑之下的，既要put效率很高，同时也要get效率很高，红黑树就是其中一种。</p>
<h2 id="红黑树的特性"><a href="#红黑树的特性" class="headerlink" title="红黑树的特性"></a><strong>红黑树的特性</strong></h2><ol>
<li><p>节点是红色或黑色；</p>
</li>
<li><p>根节点是黑色；</p>
</li>
<li><p>每个叶子节点都是黑色的空节点（NIL节点）；</p>
</li>
</ol>
<p>4  每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)；</p>
<ol>
<li><p>从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点；</p>
</li>
<li><p>每次新插入的节点都必须是红色。</p>
</li>
</ol>
<p>红黑树从根节点到叶子节点的最长路径不会超过最短路径的两倍</p>
<p>由于父节点27是红色节点，因此这种情况打破了红黑树的规则4（每个红色节点的两个子节点都是黑色），必须进行调整，使之重新符合红黑树的规则。</p>
<p>常用的调整方法有三种：</p>
<ol>
<li><p>左旋转</p>
</li>
<li><p>右旋转</p>
</li>
<li><p>变色</p>
</li>
</ol>
<p>红黑树的应用：</p>
<ol>
<li><p>TreeSet</p>
</li>
<li><p>TreeMap</p>
</li>
<li><p>HashMap（JDK8）</p>
</li>
</ol>
<h2 id="ConcurrentHashMap的实现原理"><a href="#ConcurrentHashMap的实现原理" class="headerlink" title="ConcurrentHashMap的实现原理"></a><strong>ConcurrentHashMap的实现原理</strong></h2><p>线程安全的实现机制：</p>
<ol>
<li><p>初始化数组或头节点时，ConcurrentHashMap并没有加锁，而是CAS的方式进行原子替换（原子操作，基于Unsafe类的原子操作API）。</p>
</li>
<li><p>插入数据时会进行加锁处理，但锁定的不是整个数组，而是槽中的头节点。所以，ConcurrentHashMap中锁的粒度是槽，而不是整个数组，并发的性能很好。</p>
</li>
<li><p>扩容时会进行加锁处理，锁定的仍然是头节点。并且，支持多个线程同时对数组扩容，提高并发能力。每个线程需先以CAS操作抢任务，争抢一段连续槽位的数据转移权。抢到任务后，该线程会锁定槽内的头节点，然后将链表或树中的数据迁移到新的数组里。</p>
</li>
<li><p>查找数据时并不会加锁，所以性能很好。另外，在扩容的过程中，依然可以支持查找操作。如果某个槽还未进行迁移，则直接可以从旧数组里找到数据。如果某个槽已经迁移完毕，但是整个扩容还没结束，则扩容线程会创建一个转发节点存入旧数组，届时查找线程根据转发节点的提示，从新数组中找到目标数据。</p>
</li>
</ol>
<p>加分回答：<br><figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ConcurrentHashMap实现线程安全的难点在于多线程并发扩容，即当一个线程在插入数据时，若发现数组正在扩容，那么它就会立即参与扩容操作，完成扩容后再插入数据到新数组。在扩容的时候，多个线程共同分担数据迁移任务，每个线程负责的迁移数量是 (数组长度 &gt;&gt;&gt; <span class="number">3</span>) / <span class="meta">CPU</span>核心数。</span><br><span class="line"></span><br><span class="line">也就是说，为线程分配的迁移任务，是充分考虑了硬件的处理能力的。多个线程依据硬件的处理能力，平均分摊一部分槽的迁移工作。另外，如果计算出来的迁移数量小于<span class="number">16</span>，则强制将其改为<span class="number">16</span>，这是考虑到目前服务器领域主流的<span class="meta">CPU</span>运行速度，每次处理的任务过少，对于<span class="meta">CPU</span>的算力也是一种浪费。</span><br></pre></td></tr></table></figure></p>
<h2 id="ConcurrentHashMap的扩容机制"><a href="#ConcurrentHashMap的扩容机制" class="headerlink" title="ConcurrentHashMap的扩容机制"></a><strong>ConcurrentHashMap的扩容机制</strong></h2><p>1.7版本</p>
<ol>
<li><p>1.7版本的ConcurrentHashMap是基于Segment分段实现的</p>
</li>
<li><p>每个Segment相对于一个小型的HashMap</p>
</li>
<li><p>每个Segment内部会进行扩容，和HashMap的扩容逻辑类似</p>
</li>
<li><p>先生成新的数组，然后转移元素到新数组种</p>
</li>
<li><p>扩容的判断也是每个Segment内部独立判断，判断是否超过阈值</p>
</li>
</ol>
<p>1.8版本</p>
<ol>
<li><p>1.8版本的ConcurrentHashMap不再基于Segment实现</p>
</li>
<li><p>当某个线程进行put时，如果发现ConcurrentHashMap正在进行扩容那么该线程一起扩容</p>
</li>
<li><p>如果某个线程put时，发现没有正在进行扩容，则将key-value添加到ConcurrentHashMap中，然后判断是否超过阈值，超过了则进行扩容</p>
</li>
<li><p>ConcurrentHashMap是支持多个线程同时扩容的</p>
</li>
<li><p>扩容之前也先生成一个新的数组</p>
</li>
<li><p>在转移元素时，先将原数组分组，将每组分给不同的线程来进行元素的转移，每个线程负责一组或多组的元素转移工作</p>
</li>
</ol>
<h2 id="ConcurrentHashMap原理，在jdk7和jdk8版本的区别"><a href="#ConcurrentHashMap原理，在jdk7和jdk8版本的区别" class="headerlink" title="ConcurrentHashMap原理，在jdk7和jdk8版本的区别"></a><strong>ConcurrentHashMap原理，在jdk7和jdk8版本的区别</strong></h2><p>jdk7:<br>数据结构：ReentrantLock+Segment+HashEntry,一个Segment中包含一个HashEntry数组，每个HashEntry又是一个链表结构(这个结构保证的线程安全)。</p>
<p>锁：Segment分段锁 Segment继承了ReentrantLock,锁定操作的Segment，其他的Segment不受影响，并发度为segment个数，可以通过构造函数指定，数组扩容不会影响其他的segment。</p>
<p>元素查询：二次hash，第一次Hash定位到Segment，第二次Hash定位到元素所在的链表的头部(数组下标)</p>
<p>get方法无需加锁，volatile保证</p>
<p>jdk8:<br>数据结构：synchronized+CAS(乐观锁)+数组(Node) + 链表(当链表长度大于8时，链表结构转为红黑二叉树)结构，Node的val和next都用volatile修饰，保证可见性。</p>
<p>查找，替换，赋值操作都是用CAS</p>
<p>扩容、Hash冲突的时候CAS不能保证安全，所以使用synchronized</p>
<p>锁：锁链表(Node)的head节点，不像jdk7锁的是一个segment，所以不影响其他元素的读写，锁粒度更细，效率更高，扩容时，阻塞所有的读写操作、并发扩容</p>
<p>读操作无锁：</p>
<ul>
<li>Node的val和next使用volatile修饰，读写线程对该变量互相可见</li>
<li>数组用volatile修饰，保证扩容时被读线程感知</li>
</ul>
<p><a target="_blank" rel="noopener" href="https://baijiahao.baidu.com/s?id=1724634306266520505&amp;wfr=spider&amp;for=pc">https://baijiahao.baidu.com/s?id=1724634306266520505&amp;wfr=spider&amp;for=pc</a></p>
<h2 id="ArrayList和LinkedList的区别"><a href="#ArrayList和LinkedList的区别" class="headerlink" title="ArrayList和LinkedList的区别"></a><strong>ArrayList和LinkedList的区别</strong></h2><p>ArrayList:基于<strong>动态数组</strong>，连续内存存储(数组特性),适合下标访问(随机访问)。扩容机制：因为数组长度固定，超出长度存数据时需要新建数组，然后将老数组的数据拷贝到新数组，如果不是尾部插入数据还会涉及到元素的移动(往后复制一份，插入新元素)，使用尾插法并指定初始容量可以极大提高性能、甚至超过LinkedList(需要创建大量Node对象)。</p>
<p>LinkedList:基于链表，可以存储在分散的内存中，适合做数据插入和删除操作，不适合查询:需要逐一遍历，<strong>遍历LinkedList必须使用iterator不能使用for循环，因为每次for循环体内通过get(i)取得某一元素时都需要对list重新进行遍历，性能消耗极大。</strong>另外不要试图使用indexOf等返回元素索引，并利用其进行遍历，使用indexOf对list进行遍历，当结果为空时会遍历整个列表。</p>
<h2 id="集合框架中的线程安全类你知道哪些"><a href="#集合框架中的线程安全类你知道哪些" class="headerlink" title="集合框架中的线程安全类你知道哪些"></a><strong>集合框架中的线程安全类你知道哪些</strong></h2><p>Vector、Stack、Hashtable、java.util.concurrent包下所有的集合类都是线程安全的</p>
<h3 id="如何实现一个IOC容器"><a href="#如何实现一个IOC容器" class="headerlink" title="如何实现一个IOC容器"></a><strong>如何实现一个IOC容器</strong></h3><ol>
<li><p>配置文件配置包扫描路径</p>
</li>
<li><p>递归包扫描获取.class文件</p>
</li>
<li><p>反射、确定需要交给IOC管理的类</p>
</li>
<li><p>对需要注入的类进行依赖注入</p>
</li>
</ol>
<ul>
<li>配置文件中指定需要扫描的包路径</li>
<li>定义一些注解，分别表示访问控制层、业务服务层、数据持久层、依赖注入层、获取配置文件注解。</li>
<li>从配置文件中获取需要扫描的包路径，获取到当前路径下的文件信息及文件夹信息，我们将当前路径下所有以.class结尾的文件添加到一个Set集合中进行存储</li>
<li>遍历这个Set集合，获取在类上有指定注解的类，并将其交给IOC容器，定义一个安全的Map用来存储这些对象。</li>
<li>遍历这个IOC容器，获取到每一个类的实例，判断里面是否有依赖其他的类的实例，然后进行依赖注入。</li>
</ul>
<h3 id="什么是字节码？采用字节码的好处是什么"><a href="#什么是字节码？采用字节码的好处是什么" class="headerlink" title="什么是字节码？采用字节码的好处是什么"></a><strong>什么是字节码？采用字节码的好处是什么</strong></h3><p>java中的编译器和解释器:</p>
<p>Java中引入了虚拟机的概念，即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口</p>
<p>编译程序只需要面向虚拟机，生成虚拟机能够理解的代码，然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中，这种供虚拟机理解的代码叫做字节码(即扩展名为.class的文件)，它不面向任何特定的处理器，只面向虚拟机。</p>
<p>每一种平台的解释器是不同的，但是实现的虚拟机是相同的。Java源程序经过编译器编译后生成字节码，字节码由虚拟机解释执行，虚拟机将每一条要执行的字节码送给解释器，解释器将其翻译成特定机器上的机器码，然后在特定的机器上运行。这也就解释了Java的编译与解释并存的特点。</p>
<p>Java源代码—-&gt;编译器—-&gt;jvm可执行的Java字节码(即虚拟指令)—-&gt;jvm——&gt;jvm中的解释器——&gt;机器可执行的二进制机器码——&gt;程序运行</p>
<p><strong>采用字节码的好处:</strong></p>
<p>Java语言通过字节码的方式，在一定程度上解决了传统解释型语言执行效率低的问题(传统解释型语言是解释一句执行一句，而Java是先把代码编译好，再一起解释，这样效率高)，同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效，而且，由于字节码并不专对一种特定的机器，因此，Java程序无须重新编译便可在多种不同的计算机上运行。</p>
<h3 id="什么是面向对象？谈一谈你对面向对象的理解"><a href="#什么是面向对象？谈一谈你对面向对象的理解" class="headerlink" title="什么是面向对象？谈一谈你对面向对象的理解"></a><strong>什么是面向对象？谈一谈你对面向对象的理解</strong></h3><p>什么是面向对象？</p>
<p>对比面向过程，是两种不同的处理问题的角度</p>
<p>面向过程更注重事情的每一个步骤及顺序，面向对象更注重事情有哪些参与者(对象)及各自需要做什么</p>
<p>比如：洗衣机洗衣服</p>
<p>面向过程会将任何拆解成一系列的步骤(函数)。1、打开洗衣机———&gt;2、放衣服————&gt;3、放洗衣粉————&gt;4、清洗—————&gt;5、烘干</p>
<p>面向对象会拆出人和洗衣机两个对象：</p>
<p>人：打开洗衣机  放衣服  放洗衣粉</p>
<p>洗衣机：清洗  烘干</p>
<p>从以上例子看出，面向过程比较直接高效，而面向对象更易于复用、扩展和维护</p>
<p>面向对象</p>
<p><strong>封装</strong>：封装的意义，在于明确标识出允许外部使用的所有成员函数和数据项</p>
<p>内部细节对外部调用透明，外部调用无须修改或者关心内部实现</p>
<p>1、javabean的属性私有，提供getset对外访问，因为属性的赋值或者获取逻辑只能由javabean本身决定。而不能由外部胡乱修改。<br><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="title class_">String</span> name;</span><br><span class="line"><span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">setName</span>(<span class="params"><span class="built_in">String</span> name</span>)&#123;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">name</span> = <span class="string">&quot;tuling_&quot;</span> + name;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//该name有自己的命名规则，明显不能由外部直接赋值</span></span><br></pre></td></tr></table></figure></p>
<p>2、orm框架</p>
<p>操作数据库，我们不需要关心链接是怎么建立的、sql是如何执行的，只需要引入mybatis调用方法访问即可</p>
<p><strong>继承</strong>：继承基类的方法，并做出自己的改变和/扩展</p>
<p>子类共性的方法或者属性直接使用父类的，而不需要自己再定义，只需扩展自己个性化的</p>
<p><strong>多态</strong>：基于对象所属类的不同，外部对同一个方法的调用，实际执行的逻辑不同</p>
<p>多态三个条件：继承，方法重写，父类引用指向子类对象<br><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">父类类型 变量名 <span class="operator">=</span> new 子类对象<span class="comment">;</span></span><br><span class="line">变量名.方法名()<span class="comment">;</span></span><br></pre></td></tr></table></figure><br>多态弊端:无法调用子类特有的功能</p>
<h3 id="双亲委派模型"><a href="#双亲委派模型" class="headerlink" title="双亲委派模型"></a><strong>双亲委派模型</strong></h3><img src="/wang-cheng/2022/03/18/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%BA%8C/%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B.png" class title="双亲委派模型">
<p>双亲委派模型的好处:</p>
<ul>
<li><p>主要为了安全性，避免用户自己编写的类动态替换Java的一些核心类，比如String</p>
</li>
<li><p>同时也避免了类的重复加载，因为JVM中区分不同类，不仅仅是根据类名(全路径)，相同的class文件被不同的ClassLoader加载就是不同的两个类</p>
</li>
</ul>
<h3 id="java中的异常体系"><a href="#java中的异常体系" class="headerlink" title="java中的异常体系"></a><strong>java中的异常体系</strong></h3><p>Java中的所有异常都来自顶级父类Throwable</p>
<p>Throwable下有两个子类Exception和Erro</p>
<p>Error是程序无法处理的错误，一旦出现这个错误，则程序被迫停止运行</p>
<p>Exception不会导致程序停止，又分为两个部分RunTimeException运行时异常和CheckedException检查异常(例如int类型变量被赋值String)</p>
<p>RunTimeException常常发生在程序运行过程中，会导致程序当前线程执行失败。CheckedException常常发生在程序编译过程中，会导致程序编译不通过</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/18/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%80/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/18/%E9%9D%A2%E8%AF%95%E9%A2%98-%E4%B8%80/" class="post-title-link" itemprop="url">面试题(一)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-18 11:18:17" itemprop="dateCreated datePublished" datetime="2022-03-18T11:18:17+08:00">2022-03-18</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-03-25 10:38:20" itemprop="dateModified" datetime="2022-03-25T10:38:20+08:00">2022-03-25</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="面试题"><a href="#面试题" class="headerlink" title="面试题"></a><strong>面试题</strong></h1><h2 id="第一天"><a href="#第一天" class="headerlink" title="第一天"></a><strong>第一天</strong></h2><h3 id="Java类加载器有哪些"><a href="#Java类加载器有哪些" class="headerlink" title="Java类加载器有哪些"></a><strong>Java类加载器有哪些</strong></h3><p>JDK自带的三个类加载器：bootstrap ClassLoader、ExtClassLoader、AppClassLoader</p>
<p>BootstrapClassLoader是ExtClassLoader的父类加载器(不是继承关系，而是在ExtClassLoader中有一个Private属性parent，指向BootstrapClassLoader),默认负责加载%JAVA_HOME%&#x2F;lib下的jar包和class文件。</p>
<p>ExtClassLoader是AppClassLoader的父类加载器，负责加载%JAVA_HOME%lib&#x2F;ext文件夹下的jar包和class类。</p>
<p>AppClassLoader是自定义类加载器的父类，负责加载classpath下的类文件，是系统类加载器(classpath下的类——程序员写的类)、线程上下文加载器(贯穿BootStrapClassLoader、ExtClassLoader、AppClassLoader，这三个类加载器都可以去访问线程上下文类加载器)</p>
<p>(继承ClassLoader实现自定义类加载器)</p>
<h3 id="JDK、JRE、JVM三者区别和联系"><a href="#JDK、JRE、JVM三者区别和联系" class="headerlink" title="JDK、JRE、JVM三者区别和联系"></a><strong>JDK、JRE、JVM三者区别和联系</strong></h3><p>JDK:Java Develpment Kit  java开发工具</p>
<p>JRE:Runtime Enviroment Java运行时环境</p>
<p>JVM:Java Virtual Machine Java虚拟机</p>
<p><strong>JDK包括JRE和Java工具(javac、java、jconsole等开发工具)</strong></p>
<p><strong>JRE包括bin(jvm)、lib(类库)</strong></p>
<p>流程：</p>
<p>.java文件–javac编译–&gt;.class文件—–&gt;windows版&#x2F;linux版jvm—调用类库(JRE下面的lib)解释器解释class成为–&gt;机器码—-&gt;Windows&#x2F;linux运行</p>
<h3 id="x3D-x3D-和equals"><a href="#x3D-x3D-和equals" class="headerlink" title="&#x3D;&#x3D;和equals"></a><strong>&#x3D;&#x3D;和equals</strong></h3><p>&#x3D;&#x3D;对比的是栈中的值，基本数据类型是变量值，引用类型是堆中内存对象的地址</p>
<p>equals：object默认也是采用&#x3D;&#x3D;比较，通常会重写(String类型已经重写了equals方法，所以当比较两个String类型对象时，不是返回false而是返回true。也就是说，String类中被复写的equals()方法其实是比较两个字符串的内容)</p>
<p>String str1 &#x3D; “hello” &#x2F;&#x2F;str1是被定义到常量池中</p>
<p>String str2 &#x3D; new String(“hello”) &#x2F;&#x2F;str2 是被定义到堆中</p>
<p>String str3 &#x3D; str2 &#x2F;&#x2F;引用传递</p>
<p>str1 &#x3D;&#x3D; str2  false</p>
<p>str1 &#x3D;&#x3D; str3 false</p>
<p>str2 &#x3D;&#x3D; str3 true</p>
<p>str1.equals(str2) true</p>
<p>str1.equals(str3) true</p>
<p>str2.equals(str3) true</p>
<hr>
<h3 id="final的作用、为什么局部内部类和匿名内部类只能访问局部final变量"><a href="#final的作用、为什么局部内部类和匿名内部类只能访问局部final变量" class="headerlink" title="final的作用、为什么局部内部类和匿名内部类只能访问局部final变量"></a><strong>final的作用、为什么局部内部类和匿名内部类只能访问局部final变量</strong></h3><h4 id="final的作用"><a href="#final的作用" class="headerlink" title="final的作用"></a><strong>final的作用</strong></h4><p>final:修饰类(类不可以被继承)、修饰方法(表示方法不可被子类覆盖，但是可以重载)、修饰变量(表示变量一旦被赋值就不可以更改它的值)</p>
<ol>
<li>修饰成员变量<ul>
<li>如果final修饰的是类变量(必须被static修饰)，只能在静态初始化块中指定初始值或者声明该类变量时指定初始值(例如:final int b &#x3D; 0)</li>
<li>如果final修饰的是成员变量(没有static修饰)，可以在非静态初始化块、声明该变量或者构造器中执行初始值</li>
</ul>
</li>
<li>修饰局部变量<ul>
<li>系统不会为局部变量进行初始化，局部变量必须由程序员显示初始化。因此使用final修饰局部变量时，即可以在定义时指定默认值(后面的代码不能再对变量赋值)，也可以不指定默认值，而在后面的代码中对final变量赋初值(仅一次)。</li>
</ul>
</li>
<li>修饰基本类型数据和引用类型数据<ul>
<li>如果时基本数据类型的变量，则其数值一旦在初始化之后不能再改</li>
<li>如果是引用数据类型的变量，则在对其初始化之后便不能再让其指向另一个对象。<strong>但是引用的值可以改变</strong>(可以修改指向对象的值，不可以修改指向的对象)。</li>
</ul>
</li>
</ol>
<h4 id="为什么局部内部类和匿名内部类只能访问局部final变量"><a href="#为什么局部内部类和匿名内部类只能访问局部final变量" class="headerlink" title="为什么局部内部类和匿名内部类只能访问局部final变量"></a><strong>为什么局部内部类和匿名内部类只能访问局部final变量</strong></h4><p>编译之后生成两个class文件——Test.class  Test1.class</p>
<figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Test</span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span>(<span class="params">String[] args</span>)</span>&#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//局部内部类</span></span><br><span class="line">    <span class="comment">//局部final变量a,b</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span>(<span class="params">final <span class="built_in">int</span> b</span>)</span>&#123;</span><br><span class="line">        final <span class="built_in">int</span> a = <span class="number">10</span>;</span><br><span class="line">        <span class="comment">//匿名内部类</span></span><br><span class="line">        <span class="keyword">new</span> Thread()&#123;</span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span>()</span>&#123;</span><br><span class="line">                System.<span class="keyword">out</span>.println(a);</span><br><span class="line">                System.<span class="keyword">out</span>.println(b);</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//a,b必须加final参数</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title">Outclass</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="built_in">int</span> age = <span class="number">12</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">outPrint</span>(<span class="params">final <span class="built_in">int</span> x</span>)</span>&#123;</span><br><span class="line">        <span class="comment">//局部内部类</span></span><br><span class="line">        Class InClass&#123;</span><br><span class="line">            <span class="function">pubic <span class="keyword">void</span> <span class="title">Inprint</span>()</span>&#123;</span><br><span class="line">                System.<span class="keyword">out</span>.println(x);</span><br><span class="line">                System.<span class="keyword">out</span>.println(age);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">new</span> InClass.Inprint();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>首先需要明确：内部类和外部类是处于同一个级别，内部类不会因为定义在方法中就会随着方法的执行完毕就被销毁</p>
<p>这里就产生一个问题：当外部类的方法结束时，局部变量就会被销毁了，但是内部类对象可能还存在(只有没有人再引用它时，才会死亡)。这里就出现了一个矛盾：内部类对象访问了一个不存在的变量。为了解决这个问题，就将局部变量复制了一份作为内部类的成员变量，这样当局部变量死亡后，内部类扔可以访问它，实际访问的是局部变量的“copy”。这样就好像延长了局部变量的生命周期。</p>
<p>将局部变量复制为内部类的成员变量时，必须保证这两个变量是一样的，也就是如果我们在内部类中修改了成员变量，方法中的局部变量也得跟着改变，怎么解决这个问题？</p>
<p>就将局部变量设置为final，对它初始化后，我就不让你再去修改这个变量，就保证了内部类的成员变量和方法的局部变量的一致性。这实际上也是一种妥协。使得局部变量与内部类建立的拷贝保持一直。</p>
<h3 id="String、StringBuffer、StringBuilder区别以及使用场景"><a href="#String、StringBuffer、StringBuilder区别以及使用场景" class="headerlink" title="String、StringBuffer、StringBuilder区别以及使用场景"></a><strong>String、StringBuffer、StringBuilder区别以及使用场景</strong></h3><p>String是final修饰的，不可变，每次操作都会产生新的String对象</p>
<p>StringBuffer和StringBuilder都是在原对象上操作</p>
<p>StringBuffer是线程安全的，StringBuilder线程不安全的</p>
<p>StringBuffer方法都是synchronized修饰的</p>
<p>性能：StringBuilder &gt; StringBuffer &gt; String</p>
<p>任何场景:经常需要改变字符串内容时使用后面两个(StringBuffer、StringBuilder)</p>
<p>优先使用StringBuilder,多线程使用共享变量时使用StringBuffer。</p>
<h3 id="重载和重写的区别"><a href="#重载和重写的区别" class="headerlink" title="重载和重写的区别"></a><strong>重载和重写的区别</strong></h3><p>重载:发生在同一个类中，方法名必须相同，参数类型不同、个数不同、顺序不同，方法返回值和访问修饰符可以不同，发生在编译时。<br>重写:发生在子父类，方法名、参数列表必须相同，返回值范围小于等于父类，抛出的异常范围小于等于父类，访问修饰符范围大于等于父类；如果父类方法访问修饰符为private，则子类就不能重写该方法</p>
<figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="built_in">add</span>(<span class="type">int</span> a,<span class="built_in">String</span> b)</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="built_in">String</span> <span class="built_in">add</span>(<span class="type">int</span> a,<span class="built_in">String</span> b)</span><br><span class="line"><span class="comment">//编译报错</span></span><br><span class="line"><span class="comment">//因为只有当参数类型不同/个数不同/顺序不同时，才叫重载。</span></span><br><span class="line"><span class="comment">//如果这个方法是重载方法，那么方法返回值和访问修饰符可以不同。如果这个方法不是重载方法，则会报错。</span></span><br></pre></td></tr></table></figure>

<h3 id="接口和抽象类的区别"><a href="#接口和抽象类的区别" class="headerlink" title="接口和抽象类的区别"></a><strong>接口和抽象类的区别</strong></h3><ul>
<li>抽象类可以存在普通成员函数，而接口只能存在public abstract方法</li>
<li>抽象类中的成员变量可以是各种类型的，而接口中的成员变量只能是public static final类型的</li>
<li>抽象类只能继承一个，接口可以实现多个。</li>
</ul>
<p>接口的设计目的，是对类的行为进行约束(更准确说是一种“有”约束，因为接口不能规定类不可以有什么行为)，也就是提供一种机制，可以强制要求不同的类具有相同的行为。它只约束了行为的有无，但不对如何实现行为进行限制</p>
<p>而抽象类的设计目的，是代码复用，当不同的类具有某些相同的行为(记为行为集合A)，且其中一部分行为的实现方式一致时(A的非真子集，记为B)，可以让这些类都派生于一个抽象类(先有子类，再有父类。一些类的一些共性抽取出来组成一个父类，才存在一个抽象类)，在这个抽象类中实现了B(对B方法进行了实现，而在各个子类中不能再实现B方法了)，避免让所有子类都来实现B。而A-B的部分，留给各个子类自己实现。正是因为A-B在这里没有实现，所以抽象类不允许实例化出来(因为抽象类中没有实例化的方法A-B没有实现，所以如果将抽象类实例化出来的话，调用到A-B的方法时，程序将无法再继续执行了，如果想实例化抽象类，必须在实例化的同时实现A-B的方法，否则抽象类就只能用于继承了)。</p>
<p>抽象类是对类本质的抽象，表达的是is a的关系，比如:BWM is a car。抽象类包含并实现子类的通用特性，将子类存在差异化的特性进行抽象，交由子类去实现。</p>
<p>接口是对行为的抽象，表达的是like a的关系。比如Brid like a Aircraft，但其本质上is a Bird。接口的核心是定义行为，即实现类可以做什么，至于实现类主体是谁、是如何实现的，接口并不关心。</p>
<p>使用场景：当你关注一个事物本质的时候，用抽象类；当你关注一个操作的时候，用接口。</p>
<p>抽象类的功能要远超接口，但是定义抽象类的代码很高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类(抽象类)中，你必须继承或者编写出其所有子类所有共性，虽然接口在功能上会弱化很多，但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计上会降低难度。</p>
<h3 id="List和Set的区别"><a href="#List和Set的区别" class="headerlink" title="List和Set的区别"></a><strong>List和Set的区别</strong></h3><p>List:有序，按对象进入的顺序保存对象，可重复，允许多个NULL元素对象，可以使用Iterator取出所有元素，再逐一遍历，还可以使用get(int index)获取指定下标的元素。</p>
<p>Set:无序，不可重复，最多允许有一个NULL元素对象，取元素时只能用Iterator接口取出所有元素，再逐一遍历各个元素。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/09/Spring%E6%95%B4%E5%90%88Mybatis/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/09/Spring%E6%95%B4%E5%90%88Mybatis/" class="post-title-link" itemprop="url">Spring整合Mybatis</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-09 14:26:28" itemprop="dateCreated datePublished" datetime="2022-03-09T14:26:28+08:00">2022-03-09</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-03-10 16:23:18" itemprop="dateModified" datetime="2022-03-10T16:23:18+08:00">2022-03-10</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="Spring-整合Mybatis"><a href="#Spring-整合Mybatis" class="headerlink" title="Spring 整合Mybatis"></a><strong>Spring 整合Mybatis</strong></h1><p>目前大部分的Java互联网项目都是用<code>Spring</code> + <code>SpringMVC</code> + <code>Mybatis</code>搭建的。</p>
<p><code>Mybatis</code>和<code>Spring</code>两大框架已经成为Java互联网技术主流框架，得到了广泛应用。</p>
<h2 id="Spring整合Mybatis步骤"><a href="#Spring整合Mybatis步骤" class="headerlink" title="Spring整合Mybatis步骤"></a><strong>Spring整合Mybatis步骤</strong></h2><p>1、 引入依赖：</p>
<pre><code>1. Mybatis包依赖
2. Spring的jar包
3. Mybatis与Spring的整合jar包
4. mysql-connector-java-5.1.21.jar
</code></pre>
<p>2、 编写Spring配置文件</p>
<figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">       <span class="attr">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span></span><br><span class="line"><span class="tag"><span class="language-xml">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span></span><br><span class="line"><span class="string"><span class="tag"><span class="language-xml">       http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span></span><br><span class="line"><span class="string"><span class="tag"><span class="language-xml">       http://www.springframework.org/schema/context</span></span></span></span><br><span class="line"><span class="string"><span class="tag"><span class="language-xml">       http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">    <span class="comment">&lt;!-- 加载配置文件 --&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">&quot;classpath:db.properties&quot;</span>/&gt;</span></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">    <span class="comment">&lt;!-- 配置数据源 --&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.springframework.jdbc.datasource.DriverManagerDataSource&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;driverClassName&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$</span></span></span><span class="template-variable">&#123;jdbc.driver&#125;</span><span class="language-xml"><span class="tag"><span class="string">&quot;</span>/&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;url&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$</span></span></span><span class="template-variable">&#123;jdbc.url&#125;</span><span class="language-xml"><span class="tag"><span class="string">&quot;</span>/&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;username&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$</span></span></span><span class="template-variable">&#123;jdbc.username&#125;</span><span class="language-xml"><span class="tag"><span class="string">&quot;</span>/&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;password&quot;</span> <span class="attr">value</span>=<span class="string">&quot;$</span></span></span><span class="template-variable">&#123;jdbc.password&#125;</span><span class="language-xml"><span class="tag"><span class="string">&quot;</span>/&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"></span></span><br><span class="line"><span class="language-xml">    <span class="comment">&lt;!-- sqlSessionFactory --&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;sqlSessionFactory&quot;</span> <span class="attr">class</span>=<span class="string">&quot;org.mybatis.spring.SqlSessionFactoryBean&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="comment">&lt;!-- 加载 MyBatis 的配置文件 --&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;configLocation&quot;</span> <span class="attr">value</span>=<span class="string">&quot;mybatis/SqlMapConfig.xml&quot;</span>/&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="comment">&lt;!-- 数据源 --&gt;</span></span></span><br><span class="line"><span class="language-xml">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;dataSource&quot;</span> <span class="attr">ref</span>=<span class="string">&quot;dataSource&quot;</span>/&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span></span><br></pre></td></tr></table></figure>

<p>逐一分析</p>
<p><code>&lt;context:property-placeholder&gt;</code>标签是用于读取工程中的静态属性文件，然后在该配置文件中的其他地方使用该静态属性文件中的内容时，可以采用 **${属性名}**的方式。</p>
<p>id为dataSource的bean，该bean是连接数据库的数据源(数据源是提供某种所需要数据的器件或原始媒体。在数据源中存储了所有建立数据库连接的信息。就像通过指定文件名称可以在文件系统中找到文件一样，通过提供正确的数据源名称，你可以找到相应的数据库连接)</p>
<p>设置 sqlSessionFactory 的 bean 实现类为 MyBatis 与 Spring 整合 jar 包中的 SqlSessionFactoryBean 类，在其中只需要注入两个参数：一个是 MyBatis 的全局配置文件，一个是上面配置的数据源 bean</p>
<p>4、编写Mybatis配置文件</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;</span></span><br><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">configuration</span></span></span><br><span class="line"><span class="meta">        <span class="keyword">PUBLIC</span> <span class="string">&quot;-//mybatis.org//DTD Config 3.0//EN&quot;</span></span></span><br><span class="line"><span class="meta">        <span class="string">&quot;http://mybatis.org/dtd/mybatis-3-config.dtd&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">configuration</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- settings --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">settings</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 打开延迟加载的开关 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;lazyLoadingEnabled&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 将积极加载改为消极加载（即按需加载） --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;aggressiveLazyLoading&quot;</span> <span class="attr">value</span>=<span class="string">&quot;false&quot;</span>/&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 打开全局缓存开关（二级缓存）默认值就是 true --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">setting</span> <span class="attr">name</span>=<span class="string">&quot;cacheEnabled&quot;</span> <span class="attr">value</span>=<span class="string">&quot;true&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">settings</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 别名定义 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">typeAliases</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;cn.wmyskxz.pojo&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">typeAliases</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- 加载映射文件 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">mappers</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 通过 resource 方法一次加载一个映射文件 --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">mapper</span> <span class="attr">resource</span>=<span class="string">&quot;sqlmap/UserMapper.xml&quot;</span>/&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- 批量加载mapper --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">package</span> <span class="attr">name</span>=<span class="string">&quot;cn.wmyskxz.mapper&quot;</span>/&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">mappers</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">configuration</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>在 mappers 配置中，使用 mapper 标签配置了即将要加载的 Mapper 映射文件的资源路径，当然也可以使用 package 标签，配置 mapper 代理接口所在的包名，以批量加载 mapper 代理对象。</p>
<p>5、编写映射(Mapper)文件以及其他配置文件</p>
<figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="language-xml"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="meta">&lt;!DOCTYPE <span class="keyword">mapper</span></span></span></span><br><span class="line"><span class="meta"><span class="language-xml">        <span class="keyword">PUBLIC</span> <span class="string">&quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;</span></span></span></span><br><span class="line"><span class="meta"><span class="language-xml">        <span class="string">&quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">&quot;test&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">&quot;findUserById&quot;</span> <span class="attr">parameterType</span>=<span class="string">&quot;_int&quot;</span> <span class="attr">resultType</span>=<span class="string">&quot;user&quot;</span>&gt;</span></span></span><br><span class="line"><span class="language-xml">    SELECT * FROM USER WHERE id = #</span><span class="template-variable">&#123;id&#125;</span><span class="language-xml"></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span></span><br></pre></td></tr></table></figure>

<p>6、编写Dao层和实体类</p>
<figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.cheng.pojo;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</span></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> int id;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">String</span> username;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* getter and setter */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight aspectj"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.cheng.dao;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cn.cheng.pojo.User;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">UserDAO</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 根据 id 查询用户信息</span></span><br><span class="line">    <span class="keyword">public</span> <span class="function">User <span class="title">findUserById</span><span class="params">(<span class="keyword">int</span> id)</span> <span class="keyword">throws</span> Exception</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>7、编写Service层并调用Dao层</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> cn.cheng.service;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> cn.cheng.dao.UserDao</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserServiceimpl</span> <span class="keyword">implements</span>  <span class="title class_">UserService</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Autowire()</span></span><br><span class="line">    UserDao userDao;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> User <span class="title function_">findUserById</span><span class="params">(<span class="type">int</span> id)</span>&#123;</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> userDao.findUserById(id);</span><br><span class="line">        <span class="keyword">return</span> user;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>参考资料</strong><br><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/412051d41d73">https://www.jianshu.com/p/412051d41d73</a></p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/07/Spring%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%89/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/07/Spring%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%89/" class="post-title-link" itemprop="url">Spring学习笔记(三)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-07 18:12:19" itemprop="dateCreated datePublished" datetime="2022-03-07T18:12:19+08:00">2022-03-07</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-03-09 14:21:41" itemprop="dateModified" datetime="2022-03-09T14:21:41+08:00">2022-03-09</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="Spring开发"><a href="#Spring开发" class="headerlink" title="Spring开发"></a><strong>Spring开发</strong></h1><h2 id="使用配置文件开发"><a href="#使用配置文件开发" class="headerlink" title="使用配置文件开发"></a><strong>使用配置文件开发</strong></h2><p>使用<code>Spring</code>要先引入<code>Spring</code>的Jar包，我们这里使用Maven来引入<code>Spring</code>。</p>
<p>pom文件：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-webmvc<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>5.3.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="代码编写"><a href="#代码编写" class="headerlink" title="代码编写"></a><strong>代码编写</strong></h3><p>1、创建实体类：</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">package com.<span class="property">cheng</span>.<span class="property">pojo</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Hello</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="title class_">String</span> str;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">String</span> <span class="title function_">getStr</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> str;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">setStr</span>(<span class="params"><span class="built_in">String</span> str</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">str</span> = str;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">String</span> <span class="title function_">toString</span>(<span class="params"></span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;Hello&#123;&quot;</span> +</span><br><span class="line">                <span class="string">&quot;str=&#x27;&quot;</span> + str + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>2、编写<code>Spring</code>配置文件<code>Application-context.xml</code></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">        https://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;hello&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.cheng.pojo.Hello&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">property</span> <span class="attr">name</span>=<span class="string">&quot;str&quot;</span> <span class="attr">value</span>=<span class="string">&quot;str的值&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">property</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>3、测试</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.wang.pojo.Hello;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyTest</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="comment">// 获取spring的上下文对象</span></span><br><span class="line">        <span class="type">ApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathXmlApplicationContext</span>(<span class="string">&quot;Application-context.xml&quot;</span>);</span><br><span class="line">        <span class="comment">// 我们的对象都在spring中管理了，我们要使用，直接去取就好了</span></span><br><span class="line">        <span class="type">Hello</span> <span class="variable">hello</span> <span class="operator">=</span> (Hello) context.getBean(<span class="string">&quot;hello&quot;</span>);</span><br><span class="line">        System.out.println(hello.toString());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>4、拓展一<br><code>Spring</code>的配置文件在加载的时候，容器中管理的对象就已经被创建初始化了(具体可以参考Spring学习笔记(一) )</p>
<p>用配置文件初始化具有三种方式：</p>
<p>创建实体类：</p>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">package com.cheng.pojo;</span><br><span class="line"></span><br><span class="line"><span class="built_in">public</span> <span class="keyword">class</span> <span class="keyword">User</span> &#123;</span><br><span class="line">    private String <span class="type">name</span>;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">public</span> <span class="keyword">User</span>() &#123;</span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;User被创建了&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">public</span> String getName() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="type">name</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">public</span> <span class="type">void</span> setName(String <span class="type">name</span>) &#123;</span><br><span class="line">        this.name = <span class="type">name</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">public</span> <span class="type">void</span> <span class="keyword">show</span>()&#123;</span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;name=&quot; + <span class="type">name</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>配置<code>Application-context.xml</code>:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">        https://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--    第一种，下标赋值--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--    &lt;bean id=&quot;user&quot; class=&quot;com.cheng.pojo.User&quot;&gt;--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--        &lt;constructor-arg index=&quot;0&quot; value=&quot;诚&quot;&gt;&lt;/constructor-arg&gt;--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--    &lt;/bean&gt;--&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--    第二种方式，通过类型创建，不建议使用--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--    &lt;bean id=&quot;user&quot; class=&quot;com.cheng.pojo.User&quot;&gt;--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--        &lt;constructor-arg type=&quot;java.lang.String&quot; value=&quot;诚&quot;&gt;&lt;/constructor-arg&gt;--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--    &lt;/bean&gt;--&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!--    第三种方式，直接通过参数名来设置--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;user&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.cheng.pojo.User&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">constructor-arg</span> <span class="attr">name</span>=<span class="string">&quot;name&quot;</span> <span class="attr">value</span>=<span class="string">&quot;诚&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">constructor-arg</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;userT&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.cheng.pojo.UserT&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">bean</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>测试:</p>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.cheng.pojo.<span class="keyword">User</span>;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.ApplicationContext;</span><br><span class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</span><br><span class="line"></span><br><span class="line"><span class="built_in">public</span> <span class="keyword">class</span> Test &#123;</span><br><span class="line">    <span class="built_in">public</span> static <span class="type">void</span> main(String[] args) &#123;</span><br><span class="line"></span><br><span class="line">        // Spring容器，就类似于婚介网站！</span><br><span class="line">        ApplicationContext context = <span class="built_in">new</span> ClassPathXmlApplicationContext(&quot;Application-context.xml&quot;);</span><br><span class="line">        <span class="keyword">User</span> <span class="keyword">user</span> = (<span class="keyword">User</span>) context.getBean(&quot;user&quot;);</span><br><span class="line">        <span class="keyword">user</span>.<span class="keyword">show</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>5、扩展二<br><code>&lt;import&gt;</code>标签，一般用于团队开发，它可以将多个配置文件合成一个</p>
<p><code>Application-context.xml</code></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">        https://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">import</span> <span class="attr">resource</span>=<span class="string">&quot;Application-context1.xml&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">import</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">import</span> <span class="attr">resource</span>=<span class="string">&quot;Application-context2.xml&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">import</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">import</span> <span class="attr">resource</span>=<span class="string">&quot;Application-context3.xml&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">import</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>注意：当三个<code>Applicaiton-context.xml</code>中存在标识符(id)相同的bean时，此时只有最后一个被导入的<code>Application-context.xml</code>有效，之前的都会被覆盖掉。</p>
<hr>
<h2 id="使用注解开发"><a href="#使用注解开发" class="headerlink" title="使用注解开发"></a><strong>使用注解开发</strong></h2><p><code>spring4.0</code>之后，想要使用注解开发，必须要引入aop的包</p>
<p>在配置文件中，还要引入一个context约束</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans</span></span></span><br><span class="line"><span class="string"><span class="tag">        http://www.springframework.org/schema/beans/spring-beans.xsd</span></span></span><br><span class="line"><span class="string"><span class="tag">        http://www.springframework.org/schema/context</span></span></span><br><span class="line"><span class="string"><span class="tag">        http://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>我们在之前都是使用<code>&lt;bean&gt;</code>进行bean的注入，而在实际开发中，也会用到注解。</p>
<h3 id="Bean的配置"><a href="#Bean的配置" class="headerlink" title="Bean的配置"></a><strong>Bean的配置</strong></h3><p>1、在<code>Application-context.xml</code>配置文件中添加一个标签</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--指定注解扫描包--&gt;</span></span><br><span class="line"><span class="comment">&lt;!--这个标签会去扫描指定的路径下的所有类--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">&quot;com.cheng.pojo&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure>



<p>2、在指定的包下(com.cheng.pojo)编写类，添加注解</p>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">@Component(&quot;user&quot;)</span><br><span class="line">// 相当于配置文件中 &lt;bean id=&quot;user&quot; <span class="keyword">class</span>=&quot;当前注解的类&quot;/&gt;</span><br><span class="line"><span class="built_in">public</span> <span class="keyword">class</span> <span class="keyword">User</span> &#123;</span><br><span class="line">    <span class="built_in">public</span> String <span class="type">name</span> = &quot;诚&quot;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>3、测试</p>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">@Test</span><br><span class="line"><span class="built_in">public</span> <span class="type">void</span> test()&#123;</span><br><span class="line">    ApplicationContext applicationContext =</span><br><span class="line">        <span class="built_in">new</span> ClassPathXmlApplicationContext(&quot;Application-context.xml&quot;);</span><br><span class="line">    <span class="keyword">User</span> <span class="keyword">user</span> = (<span class="keyword">User</span>) applicationContext.getBean(&quot;user&quot;);</span><br><span class="line">    <span class="keyword">System</span>.<span class="keyword">out</span>.println(<span class="keyword">user</span>.name);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="属性注入"><a href="#属性注入" class="headerlink" title="属性注入"></a><strong>属性注入</strong></h3><p>在使用配置文件注册bean时，有三种方式为bean进行属性注入。</p>
<p>使用注解进行属性注入：<br>1、直接在属性名上面加<code>@Value(&quot;值&quot;)</code>标签</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component(<span class="params"><span class="string">&quot;user&quot;</span></span>)</span></span><br><span class="line">// 相当于配置文件中 &lt;bean <span class="built_in">id</span>=<span class="string">&quot;user&quot;</span> <span class="keyword">class</span>=<span class="string">&quot;当前注解的类&quot;</span>/&gt;</span><br><span class="line">public <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line"><span class="meta">    @Value(<span class="params"><span class="string">&quot;诚&quot;</span></span>)</span></span><br><span class="line">    // 相当于配置文件中 &lt;<span class="built_in">property</span> name=<span class="string">&quot;name&quot;</span> value=<span class="string">&quot;诚&quot;</span>/&gt;</span><br><span class="line">    public String name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>2、如果有set方法，可以在set方法上加<code>@Value(&quot;值&quot;)</code>标签</p>
<figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span>(<span class="string">&quot;user&quot;</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">public</span> <span class="title class_">String</span> name;</span><br><span class="line"> </span><br><span class="line">    <span class="meta">@Value</span>(<span class="string">&quot;秦疆&quot;</span>)</span><br><span class="line">    <span class="keyword">public</span> <span class="built_in">void</span> <span class="title function_">setName</span>(<span class="params"><span class="built_in">String</span> name</span>) &#123;</span><br><span class="line">        <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="衍生注解"><a href="#衍生注解" class="headerlink" title="衍生注解"></a>衍生注解</h3><p>Spring提供了三种与<code>@Component</code>注解功能相同的注解，利于分层</p>
<ul>
<li><code>@Controller</code>:web层</li>
<li><code>@Service</code>:service层</li>
<li><code>@Repository</code>:dao层</li>
</ul>
<h3 id="注解开发中的常用注解总结"><a href="#注解开发中的常用注解总结" class="headerlink" title="注解开发中的常用注解总结"></a>注解开发中的常用注解总结</h3><p>1、声明<strong>Bean</strong>的注解</p>
<ul>
<li><code>@Component</code>:组件，没有明确的角色</li>
<li><code>@Controller</code>:在视图层使用，控制器的声明</li>
<li><code>@Service</code>:在业务逻辑层使用（service层）</li>
<li><code>@Repository</code>:在数据访问层使用（dao层）</li>
</ul>
<p>2、注入<strong>Bean</strong>的注解</p>
<ul>
<li><code>@Autowired</code>：由Spring提供</li>
<li><code>@Resource</code>:由JSR-250提供</li>
</ul>
<p>都可以注解在属性和set方法上</p>
<p>3、java配置类相关注解</p>
<ul>
<li><code>@Configuration</code>:声明当前类为配置类，相当于xml形式的Spring配置。</li>
<li><code>@Bean</code>:注解在方法上，声明当前方法的返回值是一个Bean</li>
<li><code>@ComponentScan</code>:用于对Component进行扫描</li>
</ul>
<p>4、切面相关注解</p>
<ul>
<li><code>@Aspect</code>:声明一个切面类</li>
<li><code>@After</code>:声明被标注的方法在被调用方法执行之后执行</li>
<li><code>@Before</code>:声明被标注的方法在被调用方法执行之前执行</li>
<li><code>@Around</code>:声明被标注的方法在被调用方法执行之前、之后执行</li>
<li><code>PointCut</code>:声明切点</li>
</ul>
<p>5、@Value注解</p>
<ul>
<li><code>@Value</code>:为属性注入值(属性上)<ul>
<li>注入普通字符<code>@Value(&quot;诚&quot;)</code></li>
<li>注入操作系统属性<code>@Value(&quot;#&#123; T(java.lang.Math).random()*100&#125;&quot;)</code></li>
<li>注入其他bean属性<code>@Value(#&#123;User.name&#125;)</code></li>
<li>注入文件资源<code>@Value(&#39;classpath:com/wang/hello/test.txt&#39;)</code></li>
<li>注入网络资源<code>@Value(&quot;http://www.baidu.com&quot;)</code></li>
<li>注入配置文件<code>@Value($&#123;book.name&#125;)</code></li>
</ul>
</li>
</ul>
<hr>
<h2 id="配置文件开发和注解开发的差异"><a href="#配置文件开发和注解开发的差异" class="headerlink" title="配置文件开发和注解开发的差异"></a><strong>配置文件开发和注解开发的差异</strong></h2><ul>
<li>XML可以适用于任何场景，结构清晰，维护方便</li>
<li>注解不是自己提供的类用不了，开发简单方便</li>
</ul>
<p>各有优劣，实际开发中应根据项目需要权衡使用哪种开发方式，或结合各自的优点，整合开发一起使用。</p>
<h2 id="配置文件与注解整合开发"><a href="#配置文件与注解整合开发" class="headerlink" title="配置文件与注解整合开发"></a><strong>配置文件与注解整合开发</strong></h2><ul>
<li>xml管理Bean</li>
<li>注解完成属性注入</li>
</ul>
<h2 id="切面的使用"><a href="#切面的使用" class="headerlink" title="切面的使用"></a><strong>切面的使用</strong></h2><p>使用AOP开发，需要导入AOP的依赖包</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.aspectj<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>aspectjweaver<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.9.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>这里以注解的开发方式进行举例:</p>
<p>1、编写一个切面类(增强类)</p>
<figure class="highlight pgsql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">package com.cheng.config;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.<span class="keyword">After</span>;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.Around;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.Aspect;</span><br><span class="line"><span class="keyword">import</span> org.aspectj.lang.annotation.<span class="keyword">Before</span>;</span><br><span class="line"> </span><br><span class="line">@Aspect</span><br><span class="line"><span class="built_in">public</span> <span class="keyword">class</span> AnnotationPointcut &#123;</span><br><span class="line">    @<span class="keyword">Before</span>(&quot;execution(* com.cheng.service.UserServiceImpl.*(..))&quot;)</span><br><span class="line">    <span class="built_in">public</span> <span class="type">void</span> <span class="keyword">before</span>()&#123;</span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;---------方法执行前---------&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    @<span class="keyword">After</span>(&quot;execution(* com.cheng.service.UserServiceImpl.*(..))&quot;)</span><br><span class="line">    <span class="built_in">public</span> <span class="type">void</span> <span class="keyword">after</span>()&#123;</span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;---------方法执行后---------&quot;);</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    @Around(&quot;execution(* com.cheng.service.UserServiceImpl.*(..))&quot;)</span><br><span class="line">    <span class="built_in">public</span> <span class="type">void</span> around(ProceedingJoinPoint jp) throws Throwable &#123;</span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;环绕前&quot;);</span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;签名:&quot;+jp.getSignature());</span><br><span class="line"></span><br><span class="line">        //执行目标方法proceed</span><br><span class="line">        <span class="keyword">Object</span> proceed = jp.proceed();</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(&quot;环绕后&quot;);</span><br><span class="line">        <span class="keyword">System</span>.<span class="keyword">out</span>.println(proceed);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>2、将自定义的切面类注册进容器</p>
<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;bean <span class="attribute">id</span>=<span class="string">&quot;annotationPointcut&quot;</span> <span class="attribute">class</span>=<span class="string">&quot;com.kuang.config.AnnotationPointcut&quot;</span>/&gt;</span><br><span class="line">&lt;aop:aspectj-autoproxy/&gt;</span><br></pre></td></tr></table></figure>

<p>此时便将实现了AOP切面了。</p>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/07/Spring%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%BA%8C/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/07/Spring%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%BA%8C/" class="post-title-link" itemprop="url">Spring学习笔记(二)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
              

              <time title="创建时间：2022-03-07 11:55:03 / 修改时间：16:52:39" itemprop="dateCreated datePublished" datetime="2022-03-07T11:55:03+08:00">2022-03-07</time>
            </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="Spring-AOP"><a href="#Spring-AOP" class="headerlink" title="Spring AOP"></a><strong>Spring AOP</strong></h1><h2 id="什么是AOP"><a href="#什么是AOP" class="headerlink" title="什么是AOP"></a><strong>什么是AOP</strong></h2><p><code>AOP(Aspect Orient Programming)</code>，面向切面编程。它是面向对象编程的一种补充，主要用于处理一些具有横切性质的系统级服务，如<strong>日志收集、事务管理、安全检查、缓存、对象池管理</strong>等</p>
<p><code>AOP</code>实现的关键在于<code>AOP</code>框架自动创建的<code>AOP</code>代理，<code>AOP</code>代理则可分为静态代理和动态代理，静态代理是指使用<code>AOP</code>框架提供的命令进行编译，从而在编译阶段就可生成<code>AOP</code>代理类，因此也被成为编译时增强；而动态代理则在运行时借助于JDK动态代理、CGLib动态代理等在内存中”临时“生成<code>AOP</code>动态代理类，因此也被称为运行时增强。</p>
<p><code>AOP</code>要实现的是在我们写的代码的基础上进行一定的封装，如在方法执行前、或执行后、或执行中出现异常后这些地方进行拦截处理或叫做增强处理。</p>
<h2 id="动态代理"><a href="#动态代理" class="headerlink" title="动态代理"></a><strong>动态代理</strong></h2><ol>
<li>JDK动态代理：使用JDK的<code>Proxy</code>,<code>Method</code>,<code>InvocationHandler</code>创建代理对象，JDK动态代理要求目标类必须实现接口</li>
<li>CGLib动态代理：第三方的工具库，底层使用的是字节码技术。创建代理对象，原理是继承。通过继承目标类，创建子类，子类就是代理类，要求目标类或方法不能是final</li>
</ol>
<h2 id="动态代理的作用"><a href="#动态代理的作用" class="headerlink" title="动态代理的作用"></a><strong>动态代理的作用</strong></h2><ol>
<li>在目标类源代码不改变的基础上，增加功能。</li>
<li>减少代码的重复</li>
<li>专注业务逻辑</li>
<li>解耦合，让你的业务功能和日志、事务非业务功能分离</li>
</ol>
<h2 id="Sprng-AOP"><a href="#Sprng-AOP" class="headerlink" title="Sprng AOP"></a><strong>Sprng AOP</strong></h2><p><code>Spring AOP</code>底层是动态代理实现的，采用了JDK和CGLib两种代理方式。</p>
<h3 id="Spring-AOP代理创建的过程"><a href="#Spring-AOP代理创建的过程" class="headerlink" title="Spring AOP代理创建的过程"></a><strong>Spring AOP代理创建的过程</strong></h3><ol>
<li><p>在上一节中讲到了扫描到所有的Bean后将Bean封装成一个个的BeanDifinition，然后存入BeanDifinitionMap中，然后在从中取出依次进行实例化。在实例化之前会有一个拦截，<code>InstantiationAwareBeanPostProcessor</code>，会调用该接口的<code>postProcessBeforeInstantiation()</code>方法。</p>
</li>
<li><p>这个方法会在内部进行一系列的判断，判断当前Bean是否在advisedBeans中（保存了所有需要增强的bean）、判断当前bean是否是基础类型的<code>Advice</code>、<code>Pointcut</code>、<code>Advisor</code>、<code>AopInfrastructureBean</code>、判断是否是切面（是否实现了注解<code>@Aspect</code>）、判断是否需要跳过等(符合条件的Bean都是我们自己实现的切面类)。在判断过程中会拿到增强Bean的相关通知方法，并通过这些切面进行逻辑判断</p>
</li>
<li><p>执行完<code>postProcessBeforeInstantiation()</code>方法后执行<code>postProcessAfterInitialization</code>方法。<br>该方法会对切面进行依次包装处理，在包装过程中创建了一个代理对象(此时要代理的对象，是我们需要增强的类或方法，一般是@Service层或Controller层)</p>
</li>
<li><p>细看创建代理对象的过程，会发现在创建之前首先会遍历容器中所有的Bean，并找出声明<code>AspectJ</code>注解的类，近标记的类进行增强器的提取，最终拿到所有的增强器<code>advisor</code>(增强器中定义了切点Pointcut，通知父类Advice)并放入缓存。</p>
</li>
<li><p>创建一个代理工厂，将所有的增强器和目标类(需要增强的类)注入代理工厂，再用代理工厂创建对象。</p>
</li>
<li><p>代理工厂会选择<code>JdkDynamicAopProxy</code>或者<code>CglibAopProxy</code>，主要通过是否是接口和是否配置了<code>cglib</code>代理决定的。最终工厂会创建一个代理增强的对象。(这个增强的对象不是所谓动态代理中通过代理得到的代理类，该增强的对象里面只是保存了比如增强器、目标对象等信息，方便后面拦截器去真正的执行你所编写的AOP的功能)最终代理工厂会将创建的所有代理增强的对象全部添加到容器。以后容器中获取到的就是这个组件的代理对象。</p>
</li>
<li><p>当我们执行目标方法时，<code>CglibAopProxy.intercept()</code>会拦截目标方法的执行，根据<code>ProxyFactory</code>对象获取将要执行的目标方法拦截器链,之后，会将拦截器链和目标对象等传入methodInvocation，并调用proceed()方法。该方法执行也是拦截器的触发过程，也是目标方法的主要执行过程。通过索引遍历拦截器链中的所有的拦截器（封装的增强器），并分别执行增强方法。每次执行拦截器方法索引自增，直至所有的增强方法执行完毕。</p>
</li>
</ol>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  

      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://cheng-wang123.gitee.io/wang-cheng/2022/03/06/Spring%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%80/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/wang-cheng/images/avatar.gif">
      <meta itemprop="name" content="王诚">
      <meta itemprop="description" content="命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="诚の博客">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          
            <a href="/wang-cheng/2022/03/06/Spring%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%80/" class="post-title-link" itemprop="url">Spring学习笔记(一)</a>
        </h2>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2022-03-06 16:09:02" itemprop="dateCreated datePublished" datetime="2022-03-06T16:09:02+08:00">2022-03-06</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2022-03-19 13:58:00" itemprop="dateModified" datetime="2022-03-19T13:58:00+08:00">2022-03-19</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
          <h1 id="Spring-IOC"><a href="#Spring-IOC" class="headerlink" title="Spring IOC"></a><strong>Spring IOC</strong></h1><p>传统的程序开发过程中，如果要使用一个对象，就必须new一个出来，用完后再将这个对象销毁。</p>
<p>所谓的控制反转(IOC)，就是将我们new一个对象的工作，交给框架去完成，对于<code>Spring</code>框架来说，就是将对象的创建、销毁的控制权，都交给<code>Spring</code>来控制，也就是说控制这个对象的生命周期的不再是引用它的对象，而是变成了<code>Spring</code>。</p>
<p>所谓的依赖注入(DI)，是指动态的向某个对象提高它所需要的其他对象。比如当我们需要一个对象<code>A</code>去操作数据库，以前我们会在代码中new 一个<code>Connection</code>对象指向<code>A</code>对象。对于<code>Spring</code>框架来说，我们只需要告诉<code>Spring</code>，<code>A</code>需要一个<code>Connection</code>对象，<code>Spring</code>就会在适当的时候为我们创建一个<code>Connection</code>对象指向<code>A</code>。<code>A</code>需要依赖<code>Connection</code>对象才能运行，<code>Spring</code>将<code>Connection</code>注入到<code>A</code>,就是<strong>依赖注入</strong></p>
<h2 id="IOC容器"><a href="#IOC容器" class="headerlink" title="IOC容器"></a><strong>IOC容器</strong></h2><p><code>Spring</code>中提供了两种IOC容器，用于存放交由<code>Spring</code>控制的对象(bean)：</p>
<p><strong>BeanFactory</strong></p>
<p><strong>ApplicationContext</strong></p>
<img src="/wang-cheng/2022/03/06/Spring%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0-%E4%B8%80/IOC%E4%B8%A4%E7%A7%8D%E5%AE%B9%E5%99%A8.jpg" class title="IOC两种容器">

<p>可以看到，ApplicationContext是BeanFactory的子类。</p>
<h3 id="BeanFactory"><a href="#BeanFactory" class="headerlink" title="BeanFactory"></a><strong>BeanFactory</strong></h3><p>基础类型IOC容器。默认采用延迟初始化策略(lazy-load)，只有当客户端对象需要访问容器中某个被管理的对象时，才对该被管理对象进行初始化以及依赖注入操作。对于资源有限，并且功能要求不是很严格的场景，<code>BeanFactory</code>是比较合适的<code>IOC</code>容器选择</p>
<h3 id="ApplicationContext"><a href="#ApplicationContext" class="headerlink" title="ApplicationContext"></a><strong>ApplicationContext</strong></h3><p><code>ApplicationContext</code>是<code>BeanFactory</code>的子类，除了拥有<code>BeanFactory</code>的所有功能之外，还提供了比如事件发布、国际化信息支持等。<code>ApplicationContext</code>所管理的对象，在该容器启动之后，默认全部初始化并完成依赖注入。在那些资源充足，并且要求更多功能的场景中，<code>ApplicationContext</code>是比较合适的容器选择。</p>
<h2 id="初探IOC容器是如何工作的"><a href="#初探IOC容器是如何工作的" class="headerlink" title="初探IOC容器是如何工作的"></a><strong>初探IOC容器是如何工作的</strong></h2><p><strong>以注解方式为例子</strong></p>
<ol>
<li><p>根据配置类指定的扫描路径，扫描路径下所有class文件对象，将扫描到的class文件对象封装成类元数据信息。类元数据信息包括类的名字、父类及父类名字、实现的接口名字、内部类名字，判断是否是接口、是否是抽象类、是否是一个注解，获取类上添加的注解信息、获取方法上的注解信息、获取集合上的注解信息。</p>
</li>
<li><p>对类元数据信息进行判断，判断这些类元数据信息能不能成为一个bean。判断方式包括判断该类有没有在excludeFilters配置的排除范围内，是否有标志为bean的注解(@Component、@Bean、@Service等)等。</p>
</li>
<li><p>判断都满足后会生成对应的<code>BeanDifinition</code>对象，该对象记录了该类的的信息，包括类的名称、单例还是原型等信息。</p>
</li>
<li><p><code>BeanDifinition</code>对象就绪后，还会生成beanName，然后会去容器中判断是否存在相同的beanName，存在的话就抛出beanName已存在异常，不存在则将<code>BeanDifinition</code>注册到容器的<code>BeanDifinitionMap</code>中。</p>
</li>
<li><p>向<code>BeanDifinitionMap</code>注册完成后，会再从中取出<code>BeanDifinition</code>去实例化对象</p>
</li>
<li><p>实例化对象的第一步是先加载类，通过读取<code>BeanDifinition</code>中所对应的class，根据class进行加载。</p>
</li>
<li><p>在类加载成功后，便可以进行实例化了。但在实例化前和实例化后，可以通过实现<code>BeanPostProcessor</code>接口，在实例化前和后进行一些业务操作。</p>
</li>
<li><p>实例化。通过类去实例化一个对象。</p>
</li>
<li><p>完成了对象实例化以及实例化前后的操作，开始对属性进行依赖注入。依赖注入的逻辑比较多，简单来说就是通过判断Bean的属性中是否有对应的@Autowired、@Resource、@Value等注解,或利用构造函数，从实例化的对象的集合中取出对象进行注入。</p>
</li>
</ol>

      
    </div>

    
    
    
      <footer class="post-footer">
        <div class="post-eof"></div>
      </footer>
  </article>
  
  
  


  
  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/wang-cheng/page/2/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/wang-cheng/">1</a><a class="page-number" href="/wang-cheng/page/2/">2</a><span class="page-number current">3</span><a class="page-number" href="/wang-cheng/page/4/">4</a><a class="extend next" rel="next" href="/wang-cheng/page/4/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">王诚</p>
  <div class="site-description" itemprop="description">命运对勇士低语，你无法抵御风暴；勇士低声回应，我就是风暴</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/wang-cheng/archives/">
        
          <span class="site-state-item-count">32</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/wang-cheng/tags/">
          
        <span class="site-state-item-count">12</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">王诚</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

        
<div class="busuanzi-count">
  <script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
    <span class="post-meta-item" id="busuanzi_container_site_uv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-user"></i>
      </span>
      <span class="site-uv" title="总访客量">
        <span id="busuanzi_value_site_uv"></span>
      </span>
    </span>
    <span class="post-meta-divider">|</span>
    <span class="post-meta-item" id="busuanzi_container_site_pv" style="display: none;">
      <span class="post-meta-item-icon">
        <i class="fa fa-eye"></i>
      </span>
      <span class="site-pv" title="总访问量">
        <span id="busuanzi_value_site_pv"></span>
      </span>
    </span>
</div>








      </div>
    </footer>
  </div>

  
  <script src="/wang-cheng/lib/anime.min.js"></script>
  <script src="/wang-cheng/lib/velocity/velocity.min.js"></script>
  <script src="/wang-cheng/lib/velocity/velocity.ui.min.js"></script>

<script src="/wang-cheng/js/utils.js"></script>

<script src="/wang-cheng/js/motion.js"></script>


<script src="/wang-cheng/js/schemes/pisces.js"></script>


<script src="/wang-cheng/js/next-boot.js"></script>




  















  

  

  

</body>
</html>
